<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>KChen&#39;s Blog</title>
  
  <subtitle>硅基生命研究室的学术渣</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://kchen.cc/"/>
  <updated>2021-08-31T01:59:39.001Z</updated>
  <id>http://kchen.cc/</id>
  
  <author>
    <name>Kun Chen</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>埃隆博士和马斯克先生：特斯拉产品炼狱下的人生</title>
    <link href="http://kchen.cc/2019/01/07/elon-musk-tesla-life-inside-gigafactory/"/>
    <id>http://kchen.cc/2019/01/07/elon-musk-tesla-life-inside-gigafactory/</id>
    <published>2019-01-07T04:58:33.000Z</published>
    <updated>2021-08-31T01:59:39.001Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><blockquote><p>原文来自美国 WIRED 的<a href="https://www.wired.com/story/elon-musk-tesla-life-inside-gigafactory/" target="_blank" rel="noopener">「DR. ELON &amp; MR. MUSK: LIFE INSIDE TESLA’S PRODUCTION HELL」</a>，由阮一峰翻译。</p></blockquote><p>1、</p><p><img src="http://data.kchen.cc/bg2018122001.jpg-960" alt></p><p>1971年，马斯克出生在南非首都比勒陀利亚的一个白人家庭。母亲是加拿大人，所以他有资格申请加拿大护照。高中一毕业，他就去了加拿大。</p><p>他的最终目标是美国。在加拿大待了两年以后，1992年，他终于申请到了宾夕法尼亚大学，在那里拿了经济学本科学位，还有一个物理学的第二学位。</p><p>1995年，大学毕业后，他本想接着深造，前往加州的斯坦福大学攻读物理学博士学位。但是，入学两天就退学了。他发现，自己对学术没兴趣。</p><p><img src="http://data.kchen.cc/bg2018122002.jpg-960" alt></p><p>退学以后，他与朋友一起创业，为报纸开发互联网页面，共有160多家报纸成为了他们的客户，其中包括《纽约时报》这样的全国性大报。1999年，互联网泡沫的最高潮时期，他们的公司被康柏电脑以3.4亿美元收购。马斯克占7%的股份，拿到了2200万美元。</p><p>他拿这笔钱，又跟他人一起创办了 PayPal。2002年，eBay 以15亿美元的价格收购了 PayPal。马斯克占 11.7％ 的股份，拿到了1.65亿美元。</p><p>31岁时，他已经从一个穷光蛋变成了亿万富翁。</p><a id="more"></a><p><img src="http://data.kchen.cc/bg2018122003.jpg-960" alt></p><p>2、</p><p>发财以后，马斯克过上了奢侈的生活。但是他很快发现，比起驾驶豪华跑车，他还是更喜欢创业，做一些别人没有做过的事情。于是，在很短的一段时间内，他创办或收购了好几家看上去有点疯狂的公司：火箭公司 SpaceX、太阳能公司 Solar City、高速隧道公司 Boring Co.等等。</p><p>2004年，电动汽车公司特斯拉寻找外部投资者。那时，特斯拉还是一家小公司，只有几十个人，也没有任何产品。但是，马斯克一眼就看中了它，投了630万美元，成为该公司的最大股东，当上了董事长。后来，他觉得董事长还不过瘾，亲自下场，把创始人赶走，自己变成首席执行官和产品架构师。</p><p>2006年，他在一份文件写道：”特斯拉汽车公司的首要目标，也是我为公司提供资金的原因，就是加快让世界从化石能源转向太阳能。……我们不会停下来，直到路上的每辆车都是电动的。”</p><p>马斯克的个性在特斯拉汽车上面得到了充分体现。他坚持己见，要求完美，以非传统的方式思考，提出一些别人觉得匪夷所思的要求。他相信，只要能够简化为合乎逻辑的步骤，不可能的事情就会变成可能。</p><p><img src="http://data.kchen.cc/bg2018122004.gif-960" alt></p><p>一个典型的例子就是特斯拉汽车的门把手。马斯克坚持认为，把手可以与车身在一个平面上，当用户要开门的时候，把手会像魔术一样滑出来。工程师一致认为，这个想法很疯狂，会带来复杂的设计，而且不必要。但无论别人多么强烈反对，马斯克都不屈服。最终，这个门把手成为了特斯拉汽车的标志，让用户一接触就觉得，这种汽车很人性化，是未来世界的一部分。</p><p>这就是马斯克，他想要实现不可能的事情。</p><p>3、</p><p><img src="http://data.kchen.cc/bg2018122005.jpg-960" alt></p><p>2008年，特斯拉推出第一款产品，电动跑车 Roadster。这是世界上第一辆公开发售的锂电池汽车，也是第一辆可以跑300公里的电动汽车。但是，卖得并不好，四年里面一共只卖出了2000多辆。</p><p>由于产品不成功，特斯拉公司面临资金枯竭，但是这辆车吸引了很多人的注意，谷歌创始人谢尔盖布林和拉里佩奇、奔驰、丰田、美国能源部都有意投资。特斯拉靠着这些钱，终于渡过难关。</p><p><img src="http://data.kchen.cc/bg2018122006.jpg-960" alt></p><p>2012年6月，特斯拉推出豪华型的电动车 Model S，续航里程提高到了500公里以上，使得电动车具有了汽油车一样的实用性。这个车型大获成功，立即成为全球最畅销的电动车。</p><p><img src="http://data.kchen.cc/bg2018122007.jpg-960" alt></p><p>2015年，特斯拉推出电动 SUV 车型 Model X，它的车门会像猎鹰一样向上折叠。这个车型也非常成功。</p><p>2016年，特斯拉公司的收入飙升至70亿美元，员工人数已增至18,000人。</p><p>4、</p><p>随着特斯拉的汽车越来越受欢迎，马斯克也变成了知名人士，像好莱坞明星一样开始登上娱乐版。比如，他花了7200万美元，在一个豪华小区购买了五套豪宅，装潢成不一样的风格，供自己使用。他还公开发表一些奇特的言论，比如在推特上说：”有谣言说，我正在建造一艘飞船，返回我的家乡火星，这并非完全不实。”</p><p><img src="http://data.kchen.cc/bg2018122008.jpg-960" alt></p><p>（上图画圈的地方，都是马斯克的豪宅。）</p><p>他也非常情绪化，会因为没有达到目标而大骂员工。一位离职的高管说，”跟马斯克一起开会的时候，你坐在座位上，身体最好往下蹲一点。因为当马斯克坐得比你高的时候，他会待人友好一些。”不过，在硅谷，各种奇怪的行为都是允许的。而且，如果你是一个奇怪的人，在这里会更受欢迎。</p><p><img src="http://data.kchen.cc/bg2018122009.jpg-960" alt></p><p>2016年3月，马斯克宣布了一个新车型 Model 3，这是一辆中等价位的电动汽车，瞄准大众市场。售价预估在35,000美元左右，比 Model S 便宜一半还多。特斯拉公司希望以此证明，大规模生产电动汽车是可行的，彻底改变内燃机引擎主导的汽车工业。</p><p>当时，Model 3 还处于设计阶段，用户可以付1000美元预订，交车至少要在18个月以后。在特斯拉内部，乐观的估计是预订量会超过50,000辆，这已经接近美国汽车业的纪录。</p><p><img src="http://data.kchen.cc/bg2018122010.jpg-960" alt></p><p>2016年3月31日，星期五，Model 3 开放预订的第一天，用户可以到当地的特斯拉专卖店交订金。结果，有的地方一早就有超过2000人排队，当天全美的预订量就超过18万辆。等到周末结束，已有32.5万人预订了 Model 3。</p><p>特斯拉的股价开始飙升。那时它一共只生产了不到15万辆车，市值就已经超过通用汽车（每周就能生产15万辆车），成为美国市值最高的汽车公司。但是，特斯拉的很多高管都忧心忡忡，因为这么高的预订量意味着，特斯拉必须在很短的时间内（一两年），从一家小型汽车制造公司转变为大规模的汽车生产商，招募并培训数万名员工，有效地管理他们。这对公司的内部管理将是巨大挑战。</p><p>5、</p><p>马斯克原计划，2017年10月开始生产 Model 3，先是小批量生产，然后在较短的时期内上升到每周生产5000辆。</p><p>但是，2016年夏天，他改变了主意，想在2017年7月就开始生产，比计划提前近四个月。他还想建一个全自动化的工厂，机器人在流水线上高速地建造一切东西，传送带将每件零件及时送到正确的地方。为了达到这个目标，马斯克说，各部门都需要重新安排他们的计划，改变流水线的设计。</p><p>高管们告诉马斯克，这是不切实际的，特斯拉已经是最先进的汽车制造工厂，可以渐进式地改造，在生产运行顺利以后，逐步增加自动化程度。这么短的时间内，重新规划流水线，设计一个全新的自动化工厂将耗费大量时间和金钱，而且不一定能够达到目标。但是，马斯克坚持认为，除非违反物理定律，否则几乎任何事情都是可能的。他对高官说，”我们将构建能够构建机器的机器，而且必须快速行动。一家全自动化工厂是对特斯拉未来的投资，将有助于公司在未来几十年保持竞争力。”</p><p>接下来的几周里，高管不断与马斯克争论，工程师也提出质疑。遇到这种情况，马斯克有时会将该人的主管拉到一边，让他将该人分配到其他岗位，或者马斯克索性不再邀请质疑者参加会议，甚至直接将他解雇。</p><p>这个时期，马斯克解雇了很多人。一位前员工说，”每个人每天来上班的时候，都不知道这是否会成为他们在特斯拉的最后一天。马斯克说过，特斯拉的目标是拯救世界，与这个目标相比，个人的感受有什么重要呢。他关心的是整个人类，并不真正关心个人。”</p><p>马斯克后来估计，为了工厂改造，特斯拉每周要花费1亿美元。</p><p>6、</p><p>2017年夏天，原定的投产日期已经到了，但是 Model 3 仍然没有批量生产。工程师们仍在解决一些棘手的问题，比如让机器人识别并可靠地抓住不同颜色的电线，以及通过迷宫一样的传送带，将零件送到需要的地方。</p><p>由于远远落后于时间表，一些客户开始要求取回订金。为了平息外界的质疑，马斯克决定，7月28日召开一场 Model 3 的大型新闻发布会。特斯拉从来不在广告上花钱，而是通过发布会这种场合，产生大量的新闻报道和网络转发，以帮助销售汽车。发布会上，马斯克向前30名 Model 3 的预订者（大多数都是特斯拉的员工），交付了汽车。由于装配线还没好，这些车每一辆都是单独制造的。</p><p><img src="http://data.kchen.cc/bg2018122011.jpg-960" alt></p><p>就算到了这种时候，马斯克依然不改随便说话的脾气。那个月早些时候，他发了一条推特，声称特斯拉将在年底前每个月生产2万辆汽车。他希望通过发布会和媒体宣传，消费者可以继续对 Model 3 保持耐心。</p><p>一名前高级主管说，”马斯克是一个疯狂的天才。他的个性里面，95％是天才和5％是疯狂。但是，从那个时候开始，可能因为 Model 3 的压力，这个比例颠倒过来了，变成95%是疯狂和5%是天才。”</p><p>7、</p><p>2017年10月，Model 3 的生产远远落后于计划。目标是每周建造5,000辆汽车，但是实际上每天只能生产大约三辆汽车。</p><p><img src="http://data.kchen.cc/bg2018122012.jpg-960" alt></p><p>（上图为特斯拉在内华达州的工厂，目前世界最先进的汽车工厂。整个厂房就在一个大屋顶下面，屋顶上都是太阳能电池。）</p><p>马斯克坐不住了，整个周末都待在工厂，试图发现为什么机器不能正常工作。他的目标是工厂尽可能自动化，但是这种极端的机器人化，带来的后果是延迟和故障。特斯拉已经花费超过10亿美元建造自动化工厂，但是现在工厂几乎没有办法正常运行。</p><p>他视察生产线，如果发现问题，就找来负责的工人，为他为什么自动化没起作用。工人没有想到，能够直接跟老板对话，涨红了脸，回答支支吾吾。马斯克大发雷霆，告诉工人，在特斯拉，卓越只是基本的要求，现在你失败了，你不够聪明，不能解决这些问题，正在危害公司。他当场就把工人解雇了。</p><p>高管把马斯克拉进会议室，跟他说：”有一句谚语，被老虎追赶时，没有人能想出一个好主意。”但是，马斯克坚持自己的做法，最终解雇了700多人。他不担心招聘，因为有很多合格的人想在特斯拉工作。”这是真正改变世界的为数不多的公司之一，”一位前员工表示。</p><p>在特斯拉，所有人都知道，马斯克是领导者，最大的股东，也很有远见。有时，他会发表不切实际的长篇大论，但是你只要说错一句话，或者犯了一个错误，他就会认定你是一个白痴，而且没有什么可以改变他的想法。马斯克在会议上公开嘲笑员工，侮辱他们的能力，欺负那些未达表现的人，这是家常便饭。由于压力过大，两年内共有36位副总裁级别以上的高管离职，只有少数高管没有被替换。</p><p>不管怎么样，Model 3 装配线总算能够运行了，生产量开始上升，但是距离每周5000辆的目标，还是有很大距离。有人已经起诉特斯拉，美国司法部也开始调查，特斯拉是否故意发布错误的 Model 3 产量预测，误导投资者。</p><p>根据公司的季报，2017年的第三季度，特斯拉亏损6.71亿美元，前9个月共亏损15亿美元，仅建造了222辆 Model 3。</p><p>8、</p><p>2018年4月，马斯克公开承认，全自动化工厂的目标没有实现。为了顺利运行，工人们实际上停止了传送带，人工把汽车零件运送到需要它们的地方。当月，生产线停工一周，进行维修。</p><p>由于 Model 3 的生产太不顺利，马斯克似乎有些失控了，推特的发送频率大大加快。2016年和2017年，他每月发推特94条，但在2018年5月，他发了421条，6月414条，7月310条。推特的内容完全无拘无束，跟提出质疑的人在推特上对骂，骂对方是白痴。有一次，他甚至上传了一张日本漫画，下面写着”我实际上就是猫女，这是我的自拍”，推特关闭了他的帐户，因为怀疑被黑客入侵了，直到马斯克确认这确实是他发的。</p><p><img src="http://data.kchen.cc/bg2018122013.jpg-960" alt></p><p>2018年6月的股东大会上，马斯克说，这是他经历过的最痛苦、最恐怖的几个月。特斯拉的财务报告显示，20％的 Model 3 预订者要求退款。</p><p>马斯克开始睡到了工厂，直接解决生产中遇到的各种问题。</p><p>2018年8月，也许是因为受够了外界的质疑，马斯克发了一条推特，表示考虑将特斯拉私有化，不再作为上市公司了。特斯拉的股票价格立刻飙升。事后证明，他只是随口一说，完全没有这回事。美国证券交易委员会对马斯克罚款2000万美元，并且要求他辞去董事长职务，但他仍然是首席执行官。</p><p><img src="http://data.kchen.cc/bg2018122014.jpg-960" alt></p><p>2018年9月，在一个网络直播节目里面，马斯克当众吸食了大麻。虽然大麻在加州是合法的，但马斯克的另一家公司 SpaceX 是美国国防部的承包商，根据国防部的规定，承包商不得与毒品相关。美国宇航局宣布将对 SpaceX 进行评估，确保符合无毒品的要求。</p><p>9、</p><p>2018年7月1日，在 Model 3 开放预订两年多以后，马斯克终于向全体员工，发送了一封期待已久的电子邮件。</p><blockquote><p>“我认为我们刚刚成为了一家真正的汽车公司。我们在七天内制造了5,031辆 Model 3。”</p></blockquote><p>2018年10月，特斯拉宣布，2018年第三季度实现销售收入61亿美元，净利润3.12亿美元，远远超出华尔街的预期。</p><p><img src="http://data.kchen.cc/bg2018122015.jpg-960" alt></p><p>董事会也已经同意，未来10年内将向马斯克提供550亿美元的奖金，前提是马斯克继续领导公司，并且达到董事会设定的12个目标。其中有一个目标是市值达到6500亿美元，大约是现在的10倍。如果达不到目标，他就拿不到这笔奖金。</p><p>马斯克现在的身价已经超过200亿美元，是世界上最富有的人之一。但是，董事会愿意再给他550亿美元，确保他不离开特斯拉。</p>]]></content>
    
    <summary type="html">
    
      &lt;blockquote&gt;
&lt;p&gt;原文来自美国 WIRED 的&lt;a href=&quot;https://www.wired.com/story/elon-musk-tesla-life-inside-gigafactory/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;「DR. ELON &amp;amp; MR. MUSK: LIFE INSIDE TESLA’S PRODUCTION HELL」&lt;/a&gt;，由阮一峰翻译。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;1、&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://data.kchen.cc/bg2018122001.jpg-960&quot; alt&gt;&lt;/p&gt;
&lt;p&gt;1971年，马斯克出生在南非首都比勒陀利亚的一个白人家庭。母亲是加拿大人，所以他有资格申请加拿大护照。高中一毕业，他就去了加拿大。&lt;/p&gt;
&lt;p&gt;他的最终目标是美国。在加拿大待了两年以后，1992年，他终于申请到了宾夕法尼亚大学，在那里拿了经济学本科学位，还有一个物理学的第二学位。&lt;/p&gt;
&lt;p&gt;1995年，大学毕业后，他本想接着深造，前往加州的斯坦福大学攻读物理学博士学位。但是，入学两天就退学了。他发现，自己对学术没兴趣。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://data.kchen.cc/bg2018122002.jpg-960&quot; alt&gt;&lt;/p&gt;
&lt;p&gt;退学以后，他与朋友一起创业，为报纸开发互联网页面，共有160多家报纸成为了他们的客户，其中包括《纽约时报》这样的全国性大报。1999年，互联网泡沫的最高潮时期，他们的公司被康柏电脑以3.4亿美元收购。马斯克占7%的股份，拿到了2200万美元。&lt;/p&gt;
&lt;p&gt;他拿这笔钱，又跟他人一起创办了 PayPal。2002年，eBay 以15亿美元的价格收购了 PayPal。马斯克占 11.7％ 的股份，拿到了1.65亿美元。&lt;/p&gt;
&lt;p&gt;31岁时，他已经从一个穷光蛋变成了亿万富翁。&lt;/p&gt;
    
    </summary>
    
    
      <category term="传记" scheme="http://kchen.cc/tags/%E4%BC%A0%E8%AE%B0/"/>
    
      <category term="翻译" scheme="http://kchen.cc/tags/%E7%BF%BB%E8%AF%91/"/>
    
      <category term="产品" scheme="http://kchen.cc/tags/%E4%BA%A7%E5%93%81/"/>
    
  </entry>
  
  <entry>
    <title>如何求单链表的环入口点 - 龟兔赛跑法 - 动画解释</title>
    <link href="http://kchen.cc/2018/11/06/single-circle-linkedlist-entry-point/"/>
    <id>http://kchen.cc/2018/11/06/single-circle-linkedlist-entry-point/</id>
    <published>2018-11-06T05:42:00.000Z</published>
    <updated>2021-08-31T01:59:39.010Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>当单向链表中存在环的时候，遍历此链表会发生无限循环，无法到达末尾（入环后链表就不存在末尾了）的情况，所以在可能发生这种情况的时候，需要检查链表中是否存在一个环。</p><p>检查是链表是否存在环的方法就是「<strong>龟兔赛跑</strong>」法：乌龟和兔子同时从头节点开始遍历链表，兔子遍历的速度大于乌龟的速度，如果链表中存在环，兔子和乌龟就会先后进环，由于兔子的速度比乌龟快，他们必然会在环内相遇。</p><p>下面的动图解释了这一过程：</p><a id="more"></a><p><img src="http://data.kchen.cc/mac_af-b9a73ed3596db8a22fbc97f6f6abc35f.gif-origin" alt></p><p>这个算法的美妙之处在于，乌龟和兔子相遇的地方和环入口点的位置是有关系的。</p><p>根据动画所示，我们令兔子的遍历速度为2，乌龟的遍历速度为1，则他们的速度差也为1。设乌龟进环的时候已经遍历了 <span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.43056em;"></span><span class="strut bottom" style="height:0.43056em;vertical-align:0em;"></span><span class="base textstyle uncramped"><span class="mord mathit">x</span></span></span></span> 个节点，那么此时兔子也已经在环内遍历了 <span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.43056em;"></span><span class="strut bottom" style="height:0.43056em;vertical-align:0em;"></span><span class="base textstyle uncramped"><span class="mord mathit">x</span></span></span></span> 个节点。若令环的大小为 <span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>y</mi></mrow><annotation encoding="application/x-tex">y</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.43056em;"></span><span class="strut bottom" style="height:0.625em;vertical-align:-0.19444em;"></span><span class="base textstyle uncramped"><span class="mord mathit" style="margin-right:0.03588em;">y</span></span></span></span>，兔子和乌龟在环内的遍历就是一次追及问题，兔子需要追上乌龟的距离为 <span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>y</mi><mo>−</mo><mi>x</mi></mrow><annotation encoding="application/x-tex">y-x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.58333em;"></span><span class="strut bottom" style="height:0.7777700000000001em;vertical-align:-0.19444em;"></span><span class="base textstyle uncramped"><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="mbin">−</span><span class="mord mathit">x</span></span></span></span>。由于兔子和乌龟的速度差为1，所以追及时间 <span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>t</mi><mo>=</mo><mo>(</mo><mi>y</mi><mo>−</mo><mi>x</mi><mo>)</mo><mo>÷</mo><mn>1</mn><mo>=</mo><mi>y</mi><mo>−</mo><mi>x</mi></mrow><annotation encoding="application/x-tex">t=(y-x)\div1=y-x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.75em;"></span><span class="strut bottom" style="height:1em;vertical-align:-0.25em;"></span><span class="base textstyle uncramped"><span class="mord mathit">t</span><span class="mrel">=</span><span class="mopen">(</span><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="mbin">−</span><span class="mord mathit">x</span><span class="mclose">)</span><span class="mbin">÷</span><span class="mord mathrm">1</span><span class="mrel">=</span><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="mbin">−</span><span class="mord mathit">x</span></span></span></span>，那么乌龟和兔子相遇的点距离环入口也就是 <span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>x</mi></mrow><annotation encoding="application/x-tex">x</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.43056em;"></span><span class="strut bottom" style="height:0.43056em;vertical-align:0em;"></span><span class="base textstyle uncramped"><span class="mord mathit">x</span></span></span></span> 了。此时只需要将兔子放回起点，并把兔子的遍历速度换成1，则乌龟将会和兔子在环入口处再次相遇。</p><p>最后附上 Java 算法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">LinkedListNode <span class="title">solve</span><span class="params">(LinkedListNode head)</span> </span>&#123;</span><br><span class="line">    LinkedListNode rabbit = head;</span><br><span class="line">    LinkedListNode turtle = head;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (rabbit != <span class="keyword">null</span> &amp;&amp; rabbit.next != <span class="keyword">null</span>) &#123;</span><br><span class="line">        rabbit = rabbit.next.next;</span><br><span class="line">        turtle = turtle.next;</span><br><span class="line">        <span class="keyword">if</span> (rabbit == turtle) <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (rabbit == <span class="keyword">null</span> || rabbit.next == <span class="keyword">null</span>) <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">    rabbit = head;</span><br><span class="line">    <span class="keyword">while</span> (rabbit != turtle) &#123;</span><br><span class="line">        rabbit = rabbit.next;</span><br><span class="line">        turtle = turtle.next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> rabbit;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;当单向链表中存在环的时候，遍历此链表会发生无限循环，无法到达末尾（入环后链表就不存在末尾了）的情况，所以在可能发生这种情况的时候，需要检查链表中是否存在一个环。&lt;/p&gt;
&lt;p&gt;检查是链表是否存在环的方法就是「&lt;strong&gt;龟兔赛跑&lt;/strong&gt;」法：乌龟和兔子同时从头节点开始遍历链表，兔子遍历的速度大于乌龟的速度，如果链表中存在环，兔子和乌龟就会先后进环，由于兔子的速度比乌龟快，他们必然会在环内相遇。&lt;/p&gt;
&lt;p&gt;下面的动图解释了这一过程：&lt;/p&gt;
    
    </summary>
    
    
      <category term="Algorithm" scheme="http://kchen.cc/tags/Algorithm/"/>
    
  </entry>
  
  <entry>
    <title>Linux 常用功能性命令</title>
    <link href="http://kchen.cc/2018/06/13/linux-misc/"/>
    <id>http://kchen.cc/2018/06/13/linux-misc/</id>
    <published>2018-06-13T05:42:00.000Z</published>
    <updated>2021-08-31T01:59:39.006Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>本文章用于检索在 Linux 系统下如何解决某一个问题，每个问题通常使用一个命令的不同参数或者多个命令的组合方式来实现，命令下方给出了简单的解释。可以通过每一个小标题来寻找自己的需求。</p><a id="more"></a><h2 id="统计当前目录下特定格式文件的数目"><a href="#统计当前目录下特定格式文件的数目" class="headerlink" title="统计当前目录下特定格式文件的数目"></a>统计当前目录下特定格式文件的数目</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">find . -name <span class="string">"*.wav"</span> -<span class="built_in">print</span> | wc -l</span><br></pre></td></tr></table></figure><p>解析：<code>find .</code> 命令可以循环查找当前目录和子目录的文件，<code>-name</code> 参数用于指定文件 pattern，<code>wc -l</code> 按行统计结果。</p><h2 id="统计当前文件夹下的文件个数、目录个数"><a href="#统计当前文件夹下的文件个数、目录个数" class="headerlink" title="统计当前文件夹下的文件个数、目录个数"></a>统计当前文件夹下的文件个数、目录个数</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">ls -l |grep <span class="string">"^-"</span>|wc -l      <span class="comment">##统计当前文件夹下文件的个数</span></span><br><span class="line">ls -l |grep <span class="string">"^d"</span>|wc -l      <span class="comment">##统计当前文件夹下目录的个数</span></span><br><span class="line">ls -lR|grep <span class="string">"^-"</span>|wc -l      <span class="comment">##统计当前文件夹及子文件夹下文件的个数</span></span><br><span class="line">ls -lR|grep <span class="string">"^d"</span>|wc -l      <span class="comment">##统计文件夹及子文件夹下目录的个数</span></span><br></pre></td></tr></table></figure><p>解析：使用 <code>ls -l</code> 命令用长列表模式查看当前目录的文件（包括目录、设备文件等）<code>R</code> 参数递归子目录，<code>grep</code> 命令过滤内容，<code>^d</code> 是文件夹（目录），<code>^-</code> 是普通文件。</p><h2 id="检测本机的端口是否被占用"><a href="#检测本机的端口是否被占用" class="headerlink" title="检测本机的端口是否被占用"></a>检测本机的端口是否被占用</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 查看已开放的 tcp 端口</span></span><br><span class="line">netstat -anp tcp</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看指定端口是否被占用</span></span><br><span class="line">netstat –apn | grep 8080</span><br></pre></td></tr></table></figure><h2 id="检测目标机器-tcp-端口是否开放"><a href="#检测目标机器-tcp-端口是否开放" class="headerlink" title="检测目标机器 tcp 端口是否开放"></a>检测目标机器 tcp 端口是否开放</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">telnet www.google.com 80</span><br></pre></td></tr></table></figure><h2 id="使用-firewall-cmd-管理防火墙"><a href="#使用-firewall-cmd-管理防火墙" class="headerlink" title="使用 firewall-cmd 管理防火墙"></a>使用 firewall-cmd 管理防火墙</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">firewall-cmd --state                           <span class="comment">##查看防火墙状态，是否是running</span></span><br><span class="line">firewall-cmd --reload                          <span class="comment">##重新载入配置</span></span><br><span class="line">firewall-cmd --get-zones                       <span class="comment">##列出支持的zone</span></span><br><span class="line">firewall-cmd --get-services                    <span class="comment">##列出支持的服务，在列表中的服务是放行的</span></span><br><span class="line">firewall-cmd --query-service ftp               <span class="comment">##查看ftp服务是否支持，返回yes或者no</span></span><br><span class="line">firewall-cmd --add-service=ftp                 <span class="comment">##临时开放ftp服务</span></span><br><span class="line">firewall-cmd --add-service=ftp --permanent     <span class="comment">##永久开放ftp服务</span></span><br><span class="line">firewall-cmd --remove-service=ftp --permanent  <span class="comment">##永久移除ftp服务</span></span><br><span class="line">firewall-cmd --add-port=80/tcp --permanent     <span class="comment">##永久添加80端口 </span></span><br><span class="line">iptables -L -n                                 <span class="comment">##查看规则，这个命令是和iptables的相同的</span></span><br><span class="line">man firewall-cmd                               <span class="comment">##查看帮助</span></span><br></pre></td></tr></table></figure><h2 id="GPU-使用情况查询"><a href="#GPU-使用情况查询" class="headerlink" title="GPU 使用情况查询"></a>GPU 使用情况查询</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">nvidia-smi      <span class="comment">##查看 GPU 此时的使用情况</span></span><br><span class="line">gpustat         <span class="comment">##更加精简的结果（pip install gpustat）</span></span><br></pre></td></tr></table></figure><h2 id="Bash-脚本中的循环控制"><a href="#Bash-脚本中的循环控制" class="headerlink" title="Bash 脚本中的循环控制"></a>Bash 脚本中的循环控制</h2><p><strong>1.基本格式</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> 变量 <span class="keyword">in</span> 取值列表</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">    各种操作</span><br><span class="line"><span class="keyword">done</span></span><br></pre></td></tr></table></figure><p>还有<strong>罕见</strong>的写法就是都写作一行里：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> 变量 <span class="keyword">in</span> 取值列表 ; <span class="keyword">do</span> 各种操作 ;<span class="keyword">done</span></span><br></pre></td></tr></table></figure><p><strong>2.普通枚举</strong></p><p>取值列表为空格或回车符分割的字符串</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> <span class="string">'apple'</span> <span class="string">'meat'</span> <span class="string">'sleep'</span> <span class="string">'woman'</span></span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">    <span class="built_in">echo</span> I like <span class="variable">$i</span></span><br><span class="line"><span class="keyword">done</span></span><br></pre></td></tr></table></figure><p><strong>3.花括号迭代</strong></p><ul><li>数字迭代，比如<code>{1..100}</code></li><li>字母迭代，比如<code>{a..z},{A..Z},{Z..A}</code></li><li>ASCII字符迭代，比如<code>{a..A}</code></li></ul><p>计算1加到100的和</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line">ans=0</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> &#123;1..100&#125;</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">    <span class="built_in">let</span> ans+=<span class="variable">$i</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"><span class="built_in">echo</span> <span class="variable">$ans</span></span><br></pre></td></tr></table></figure><p>花括号的迭代还可以指定指定增量，格式如下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;首..尾..增量&#125;</span><br></pre></td></tr></table></figure><p>计算一下1到100以内的所有奇数的和：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> &#123;1..100..2&#125;</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">    <span class="built_in">echo</span> <span class="variable">$i</span></span><br><span class="line"><span class="keyword">done</span></span><br></pre></td></tr></table></figure><p><strong>4.C 风格 for 循环</strong></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/bash</span></span><br><span class="line">ans=0</span><br><span class="line"><span class="keyword">for</span> ((i=1;i&lt;=100;i++))</span><br><span class="line"><span class="keyword">do</span></span><br><span class="line">    <span class="built_in">let</span> ans+=<span class="variable">$i</span></span><br><span class="line"><span class="keyword">done</span></span><br><span class="line"><span class="built_in">echo</span> <span class="variable">$ans</span></span><br></pre></td></tr></table></figure><blockquote><p><strong>注意!!!这里的for循环要有两层括号</strong></p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本文章用于检索在 Linux 系统下如何解决某一个问题，每个问题通常使用一个命令的不同参数或者多个命令的组合方式来实现，命令下方给出了简单的解释。可以通过每一个小标题来寻找自己的需求。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Tips" scheme="http://kchen.cc/tags/Tips/"/>
    
      <category term="Linux" scheme="http://kchen.cc/tags/Linux/"/>
    
  </entry>
  
  <entry>
    <title>Linux 服务器/macOS 环境初始化</title>
    <link href="http://kchen.cc/2018/04/02/linux-startup/"/>
    <id>http://kchen.cc/2018/04/02/linux-startup/</id>
    <published>2018-04-02T12:49:00.000Z</published>
    <updated>2021-08-31T01:59:39.007Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>本手册适用于新创建的 Linux 服务器基本环境初始化，包括连接性能优化、Shell 脚本语言和 Vim 环境优化配置。</p><p>其中魔改 BBR 部分视情况开启，如果服务器需要用作高性能网络代理，可以开启提高性能，否则不必开启。<strong>BBR 开启只能在安装其他软件之前进行，若之后开启则会令安装的软件失效。</strong></p><p>如只需要对服务器环境进行配置，不涉及到网络代理；以及本地环境的初始化配置。可直接跳转到 <a href="#%E5%88%87%E6%8D%A2-Shell">¶环境配置：切换 Shell</a> 部分开始。</p><a id="more"></a><h2 id="开启-BBR-POWERED（可选）"><a href="#开启-BBR-POWERED（可选）" class="headerlink" title="开启 BBR POWERED（可选）"></a>开启 BBR POWERED（可选）</h2><p><a href="https://github.com/xratzh/CBBR" target="_blank" rel="noopener">参考并使用了 xratzh 的魔改 BBR</a>。</p><h2 id="安装-Mosh"><a href="#安装-Mosh" class="headerlink" title="安装 Mosh"></a>安装 Mosh</h2><p>用来提高 SSH 连接远程服务器的输入延时和移动设备连接的稳定性（除非手动操作，基本不会断开连接），安装完成后，直接使用 <code>mosh</code> 代替 <code>ssh</code> 即可。</p><h3 id="Debian-Ubuntu-安装-mosh"><a href="#Debian-Ubuntu-安装-mosh" class="headerlink" title="Debian/Ubuntu 安装 mosh"></a>Debian/Ubuntu 安装 mosh</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">sudo add-apt-repository ppa:keithw/mosh-dev</span><br><span class="line">sudo apt-get update</span><br><span class="line">sudo apt-get install mosh</span><br></pre></td></tr></table></figure><h3 id="CentOS7-安装-mosh"><a href="#CentOS7-安装-mosh" class="headerlink" title="CentOS7 安装 mosh"></a>CentOS7 安装 mosh</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">yum -y install epel-release</span><br><span class="line">yum makecache</span><br><span class="line">yum -y install mosh</span><br></pre></td></tr></table></figure><h3 id="Tips"><a href="#Tips" class="headerlink" title="Tips"></a>Tips</h3><p>如果在内网无法直接访问yum仓库，可以下面的目录查找mosh及依赖的rpm包</p><p><a href="http://dl.fedoraproject.org/pub/epel/7/x86_64/" target="_blank" rel="noopener">http://dl.fedoraproject.org/pub/epel/7/x86_64/</a></p><p>rpm包是安装首字母分类到各个目录的，例如mosh的rpm包在</p><p><a href="http://dl.fedoraproject.org/pub/epel/7/x86_64/m/" target="_blank" rel="noopener">http://dl.fedoraproject.org/pub/epel/7/x86_64/m/</a></p><p>安装完成后，可能需要打开防火墙的 60000-61000 UDP 端口供 mosh 使用：</p><p>Firewall-cmd：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">firewall-cmd --zone=public --add-port=60000-61000/udp --permanent</span><br><span class="line">firewall-cmd --reload</span><br></pre></td></tr></table></figure><p>iptables：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sudo iptables -I INPUT 1 -p udp --dport 60000:61000 -j ACCEPT</span><br><span class="line">sudo service iptables save</span><br></pre></td></tr></table></figure><p>如果出现语言问题，添加到 <code>bashrc</code> 或者 <code>zshrc</code>：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">export LC_ALL=&quot;en_US.UTF-8&quot;</span><br></pre></td></tr></table></figure><p>或者更妥善的解决方法是将本地（Mac）的 Shell 环境改为英文：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">export LC_ALL=en_US.UTF-8  </span><br><span class="line">export LANG=en_US.UTF-8</span><br></pre></td></tr></table></figure><h2 id="切换-Shell"><a href="#切换-Shell" class="headerlink" title="切换 Shell"></a>切换 Shell</h2><p>把默认的 bash 切换为 zsh，使用 oh-my-zsh 框架，并使用 k-zsh 配置。</p><h3 id="什么是-Zsh-和它的框架-Oh-My-Zsh"><a href="#什么是-Zsh-和它的框架-Oh-My-Zsh" class="headerlink" title="什么是 Zsh 和它的框架 Oh-My-Zsh?"></a>什么是 Zsh 和它的框架 Oh-My-Zsh?</h3><p>Oh-My-Zsh is a framework for <a href="http://www.zsh.org/" target="_blank" rel="noopener">Zsh</a>, the Z shell.</p><ul><li>为了使用 Oh-My-Zsh 框架，必须先安装 Zsh。<ul><li>运行 <code>zsh --version</code> 来确认。</li><li>如果结果是 <code>zsh 4.3.9</code> 或更高的版本则可以。</li></ul></li><li>然后，需要把 Zsh 更改为默认的 shell 语言：<ul><li>在新的 Terminal 中运行 <code>echo $SHELL</code> 来确认。</li><li>如果结果是 <code>/bin/zsh</code> 或其他 zsh 路径均可。</li></ul></li></ul><h3 id="安装并设置-Zsh-为默认-shell-语言"><a href="#安装并设置-Zsh-为默认-shell-语言" class="headerlink" title="安装并设置 Zsh 为默认 shell 语言"></a>安装并设置 Zsh 为默认 shell 语言</h3><p>如果需要的话，依照以下步骤来安装 Zsh：</p><ol><li>安装 Zsh 有两种主流的办法<ul><li>直接利用包管理工具， <em>e.g.</em> Debian/Ubuntu 下的 <code>sudo apt-get install zsh</code> 或者 CentOS 下的 <code>sudo yum update &amp;&amp; sudo yum -y install zsh</code>。</li><li>从 <a href="http://zsh.sourceforge.net/Arc/source.html" target="_blank" rel="noopener">source 源</a>安装， 依照 <a href="http://zsh.sourceforge.net/FAQ/zshfaq01.html#l7" target="_blank" rel="noopener">instructions from the Zsh FAQ</a> 进行即可。</li></ul></li><li>通过运行 <code>zsh --version</code> 来验证安装。 如果结果是 <code>zsh 4.3.9</code> 或更高的版本则可以。</li><li>更改为默认 shell： <code>chsh -s $(which zsh)</code><ul><li>注意，如果 Zsh 不在你的授权 shells 列表里面 (<code>/etc/shells</code>) 或者你没有权限使用 <code>chsh</code>，上述命令不会成功。 如果是这种情况的话， <a href="https://www.google.com/search?q=zsh+default+without+chsh" target="_blank" rel="noopener">你可以依照这个方案执行</a>。<ul><li>Create <code>.bash_profile</code> in your home directory and add these lines: <ul><li><code>export SHELL=/bin/zsh</code></li><li><code>exec /bin/zsh -l</code></li></ul></li><li>Update: <code>.profile</code> may work as a general solution when default shell is not bash. I’m not sure if <code>.profile</code> may be called by Zsh as well that it could go redundant but we can do it safely with a simple check:<ul><li><code>export SHELL=/bin/zsh</code></li><li><code>[ -z &quot;$ZSH_VERSION&quot; ] &amp;&amp; exec /bin/zsh -l</code></li></ul></li></ul></li></ul></li><li>登出并重新登入来使用新的默认 shell。</li><li>通过运行 <code>echo $SHELL</code> 来确认， 如果结果是 <code>/bin/zsh</code> 或其他 zsh 路径则安装并切换成功。</li></ol><h3 id="Oh-My-Zsh-安装"><a href="#Oh-My-Zsh-安装" class="headerlink" title="Oh My Zsh 安装"></a>Oh My Zsh 安装</h3><p>你可以在终端中通过以下任意一条命令来安装 Oh My Zsh。你可以使用 <code>curl</code> 或者 <code>wget</code>。</p><h4 id="通过-curl"><a href="#通过-curl" class="headerlink" title="通过 curl"></a>通过 curl</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/\</span><br><span class="line">oh-my-zsh/master/tools/install.sh)"</span><br></pre></td></tr></table></figure><h4 id="通过-wget"><a href="#通过-wget" class="headerlink" title="通过 wget"></a>通过 wget</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sh -c "$(wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O -)"</span><br></pre></td></tr></table></figure><h3 id="安装-k-zsh"><a href="#安装-k-zsh" class="headerlink" title="安装 k-zsh"></a>安装 k-zsh</h3><p>k-zsh 是我配置的一套 Oh My Zsh 设置，包含了常用的插件和更清晰的主题效果。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mv ~/.zshrc ~/.zshrc_bac</span><br><span class="line">curl https://raw.githubusercontent.com/kchen0x/k-zsh/master/zshrc &gt; ~/.zshrc</span><br></pre></td></tr></table></figure><p>k-zsh 中配置了部分插件需要单独安装一下才能生效：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git clone https://github.com/zsh-users/zsh-autosuggestions $&#123;ZSH_CUSTOM:-~/.oh-my-zsh/custom&#125;/plugins/zsh-autosuggestions</span><br><span class="line">git clone https://github.com/zsh-users/zsh-syntax-highlighting.git $&#123;ZSH_CUSTOM:-~/.oh-my-zsh/custom&#125;/plugins/zsh-syntax-highlighting</span><br></pre></td></tr></table></figure><p>其中常用的插件是 <code>z</code>，快速跳转到相应目录。还有 <code>x</code>，快速解压缩文件等。</p><h2 id="安装-k-vim-服务器简化版"><a href="#安装-k-vim-服务器简化版" class="headerlink" title="安装 k-vim 服务器简化版"></a>安装 k-vim 服务器简化版</h2><p>k-vim for server 是 wklken 配置的 k-vim 的服务器版本，包含了许多简单有用的设置，处于便捷考虑 <code>perfix</code> 按键已经被修改为 <code>,</code> 键。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mv ~/.vimrc ~/.vimrc_bak</span><br><span class="line">curl https://raw.githubusercontent.com/wklken/vim-for-server/master/vimrc &gt; ~/.vimrc</span><br></pre></td></tr></table></figure><p>如果需要安装完整非便携版的 k-vim，请根据 <a href="https://github.com/kchen0x/k-vim" target="_blank" rel="noopener">https://github.com/kchen0x/k-vim</a> 进行操作。</p><h2 id="安装-k-tmux"><a href="#安装-k-tmux" class="headerlink" title="安装 k-tmux"></a>安装 k-tmux</h2><p>确保使用 2.3 以上版本的 tmux： <code>tmux -V</code>。</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mv ~/.tmux.conf ~/.tmux.conf_bak</span><br><span class="line">curl https://raw.githubusercontent.com/kchen0x/k-tmux/master/tmux.conf &gt; ~/.tmux.conf</span><br></pre></td></tr></table></figure><p>安装插件管理</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm</span><br></pre></td></tr></table></figure><p>安装完成后进入 tmux，使用[prefix]加大写<code>I</code>重载一次插件即可（[prefix]为<code>ctrl+a</code>）。</p><h2 id="集群监控-ServerStatus（中文版）"><a href="#集群监控-ServerStatus（中文版）" class="headerlink" title="集群监控 ServerStatus（中文版）"></a>集群监控 ServerStatus（中文版）</h2><h3 id="自动部署"><a href="#自动部署" class="headerlink" title="自动部署"></a>自动部署</h3><p><strong>【服务端】</strong></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">wget https://raw.githubusercontent.com/cppla/ServerStatus/master/autodeploy/config.json</span><br><span class="line">docker run -d --restart=always --name=serverstatus -v &#123;$path&#125;/config.json:/ServerStatus/server/config.json -p &#123;$port&#125;:80 -p &#123;$port&#125;:35601 cppla/serverstatus</span><br><span class="line"></span><br><span class="line">eg:</span><br><span class="line">docker run -d --restart=always --name=serverstatus -v ~/config.json:/ServerStatus/server/config.json -p 80:80 -p 35601:35601 cppla/serverstatus</span><br></pre></td></tr></table></figure><p><strong>【客户端】</strong></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">wget --no-check-certificate -qO client-linux.py &apos;https://raw.githubusercontent.com/cppla/ServerStatus/master/clients/client-linux.py&apos; &amp;&amp; nohup python client-linux.py SERVER=&#123;$SERVER&#125; USER=&#123;$USER&#125; PASSWORD=&#123;$PASSWORD&#125; &gt;/dev/null 2&gt;&amp;1 &amp;</span><br><span class="line"></span><br><span class="line">eg:</span><br><span class="line">wget --no-check-certificate -qO client-linux.py &apos;https://raw.githubusercontent.com/cppla/ServerStatus/master/clients/client-linux.py&apos; &amp;&amp; nohup python client-linux.py SERVER=45.79.67.132 USER=s04  &gt;/dev/null 2&gt;&amp;1 &amp;</span><br></pre></td></tr></table></figure><h3 id="手动部署"><a href="#手动部署" class="headerlink" title="手动部署"></a>手动部署</h3><p>查看<a href="https://github.com/BotoX/ServerStatus" target="_blank" rel="noopener">官方 Github</a></p><h2 id="安装网络监控-BitMeter"><a href="#安装网络监控-BitMeter" class="headerlink" title="安装网络监控 BitMeter"></a>安装网络监控 BitMeter</h2><p>在 <a href="https://codebox.net/pages/bitmeteros/downloads" target="_blank" rel="noopener">官网下载</a> 预编译的二进制包。</p><p>安装：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo dpkg -i &lt;target.deb&gt;</span><br></pre></td></tr></table></figure><p>启动监控服务：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">sudo bmdb webremote</span><br><span class="line">sudo bmdb webstop</span><br><span class="line">sudo bmdb webstart</span><br></pre></td></tr></table></figure><p>然后默认可以在 <code>localhost:2605/index.html</code> 访问到数据。</p><p>BitMeterOS 提供了3个工具可以使用：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">bmclient -h</span><br><span class="line">bmdb <span class="built_in">help</span></span><br><span class="line">bmsync -h</span><br></pre></td></tr></table></figure><h3 id="NetHogs-软件"><a href="#NetHogs-软件" class="headerlink" title="NetHogs 软件"></a>NetHogs 软件</h3><p>同时，可以安装 NetHogs 来观察每个进程的实时网速：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">sudo apt install nethogs</span><br><span class="line"></span><br><span class="line">sudo nethogs</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本手册适用于新创建的 Linux 服务器基本环境初始化，包括连接性能优化、Shell 脚本语言和 Vim 环境优化配置。&lt;/p&gt;
&lt;p&gt;其中魔改 BBR 部分视情况开启，如果服务器需要用作高性能网络代理，可以开启提高性能，否则不必开启。&lt;strong&gt;BBR 开启只能在安装其他软件之前进行，若之后开启则会令安装的软件失效。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如只需要对服务器环境进行配置，不涉及到网络代理；以及本地环境的初始化配置。可直接跳转到 &lt;a href=&quot;#%E5%88%87%E6%8D%A2-Shell&quot;&gt;¶环境配置：切换 Shell&lt;/a&gt; 部分开始。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Linux" scheme="http://kchen.cc/tags/Linux/"/>
    
      <category term="Mac" scheme="http://kchen.cc/tags/Mac/"/>
    
      <category term="Server" scheme="http://kchen.cc/tags/Server/"/>
    
  </entry>
  
  <entry>
    <title>Linux - 查看系统信息</title>
    <link href="http://kchen.cc/2017/03/03/linux-ls-info/"/>
    <id>http://kchen.cc/2017/03/03/linux-ls-info/</id>
    <published>2017-03-03T03:28:47.000Z</published>
    <updated>2021-08-31T01:59:39.006Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>在 Linux 系统下经常要查看各种信息，命令蛮多的，而且又是久不久用一次的那种，记不下来，每回找又麻烦，干脆自己写一份在博客里面，自己找起来也方便。</p><a id="more"></a><h2 id="系统"><a href="#系统" class="headerlink" title="系统"></a>系统</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"># uname -a               # 查看内核/操作系统/CPU信息</span><br><span class="line"># head -n 1 /etc/issue   # 查看操作系统版本</span><br><span class="line"># cat /etc/issue | grep Linux   # 查看当前操作系统内核信息</span><br><span class="line"># cat /proc/cpuinfo      # 查看CPU信息</span><br><span class="line"># hostname               # 查看计算机名</span><br><span class="line"># lspci -tv              # 列出所有PCI设备</span><br><span class="line"># lsusb -tv              # 列出所有USB设备</span><br><span class="line"># lsmod                  # 列出加载的内核模块</span><br><span class="line"># env                    # 查看环境变量</span><br></pre></td></tr></table></figure><h2 id="资源"><a href="#资源" class="headerlink" title="资源"></a>资源</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"># free -m                # 查看内存使用量和交换区使用量</span><br><span class="line"># df -h                  # 查看各分区使用情况</span><br><span class="line"># du -sh &lt;dir&gt;           # 查看指定目录的大小</span><br><span class="line"># cat /proc/meminfo      # 查看内存信息</span><br><span class="line"># grep MemTotal /proc/meminfo   # 查看内存总量</span><br><span class="line"># grep MemFree /proc/meminfo    # 查看空闲内存量</span><br><span class="line"># uptime                 # 查看系统运行时间、用户数、负载</span><br><span class="line"># cat /proc/loadavg      # 查看系统负载</span><br></pre></td></tr></table></figure><h2 id="磁盘和分区"><a href="#磁盘和分区" class="headerlink" title="磁盘和分区"></a>磁盘和分区</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"># mount | column -t      # 查看挂接的分区状态</span><br><span class="line"># fdisk -l               # 查看所有分区</span><br><span class="line"># swapon -s              # 查看所有交换分区</span><br><span class="line"># hdparm -i /dev/hda     # 查看磁盘参数(仅适用于IDE设备)</span><br><span class="line"># dmesg | grep IDE       # 查看启动时IDE设备检测状况</span><br></pre></td></tr></table></figure><h2 id="网络"><a href="#网络" class="headerlink" title="网络"></a>网络</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"># ifconfig               # 查看所有网络接口的属性</span><br><span class="line"># iptables -L            # 查看防火墙设置</span><br><span class="line"># route -n               # 查看路由表</span><br><span class="line"># netstat -lntp          # 查看所有监听端口</span><br><span class="line"># netstat -antp          # 查看所有已经建立的连接</span><br><span class="line"># netstat -s             # 查看网络统计信息</span><br></pre></td></tr></table></figure><h2 id="进程"><a href="#进程" class="headerlink" title="进程"></a>进程</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># ps -ef                 # 查看所有进程</span><br><span class="line"># top                    # 实时显示进程状态</span><br></pre></td></tr></table></figure><h2 id="用户"><a href="#用户" class="headerlink" title="用户"></a>用户</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"># w                      # 查看活动用户</span><br><span class="line"># id &lt;username&gt;          # 查看指定用户信息</span><br><span class="line"># last                   # 查看用户登录日志</span><br><span class="line"># cut -d: -f1 /etc/passwd       # 查看系统所有用户</span><br><span class="line"># cut -d: -f1 /etc/group        # 查看系统所有组</span><br><span class="line"># crontab -l             # 查看当前用户的计划任务</span><br></pre></td></tr></table></figure><h2 id="服务"><a href="#服务" class="headerlink" title="服务"></a>服务</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># chkconfig --list       # 列出所有系统服务</span><br><span class="line"># chkconfig --list | grep on    # 列出所有启动的系统服务</span><br></pre></td></tr></table></figure><h2 id="程序"><a href="#程序" class="headerlink" title="程序"></a>程序</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"># rpm -qa                # 查看所有安装的软件包</span><br></pre></td></tr></table></figure><h2 id="查看-CPU-信息（型号）"><a href="#查看-CPU-信息（型号）" class="headerlink" title="查看 CPU 信息（型号）"></a>查看 CPU 信息（型号）</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c </span><br><span class="line">      8  Intel(R) Xeon(R) CPU            E5410   @ 2.33GHz</span><br></pre></td></tr></table></figure><p>看到有8个逻辑 CPU, 也知道了 CPU 型号</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"># cat /proc/cpuinfo | grep physical | uniq -c </span><br><span class="line">      4 physical id      : 0 </span><br><span class="line">      4 physical id      : 1</span><br></pre></td></tr></table></figure><p>说明实际上是两颗4核的CPU </p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># getconf LONG_BIT </span><br><span class="line">   32</span><br></pre></td></tr></table></figure><p>说明当前 CPU 运行在 32bit 模式下, 但不代表 CPU 不支持 64bit </p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"># cat /proc/cpuinfo | grep flags | grep &apos; lm &apos; | wc -l </span><br><span class="line">   8</span><br></pre></td></tr></table></figure><p>结果大于 0, 说明支持 64bit 计算. <code>lm</code>指 long mode, 支持 <code>lm</code> 则是 64bit </p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;在 Linux 系统下经常要查看各种信息，命令蛮多的，而且又是久不久用一次的那种，记不下来，每回找又麻烦，干脆自己写一份在博客里面，自己找起来也方便。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Tips" scheme="http://kchen.cc/tags/Tips/"/>
    
      <category term="Linux" scheme="http://kchen.cc/tags/Linux/"/>
    
  </entry>
  
  <entry>
    <title>Linux - 进程管理</title>
    <link href="http://kchen.cc/2017/02/22/linux-process-management/"/>
    <id>http://kchen.cc/2017/02/22/linux-process-management/</id>
    <published>2017-02-22T09:01:13.000Z</published>
    <updated>2017-02-23T02:27:35.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>在 Linux 系统中，特别是没有 GUI 的发行版本里，与进程打交道是一件家常便饭的事情，能不能把进程管理玩溜了，完全可以决定你 Linux 系统的使用体验。今天，我们来看一看常见的进程管理工具和技巧。</p><a id="more"></a><h2 id="使用-ps-命令查看进程"><a href="#使用-ps-命令查看进程" class="headerlink" title="使用 ps 命令查看进程"></a>使用 ps 命令查看进程</h2><p><code>ps</code> 命令是 process status 的简称，用于显示当前运行的进程的信息。</p><p>在不使用任何标识的情况下，<code>ps</code> 会显示所有被当前用户启动的进程，比如：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ ps</span><br><span class="line">  PID TTY           TIME CMD</span><br><span class="line">20094 ttys000    0:00.11 -bash</span><br><span class="line"> 3086 ttys002    0:00.68 -bash</span><br></pre></td></tr></table></figure><p>其中每一列代表：</p><ul><li><code>PID</code>：进程的 ID 号</li><li><code>TTY</code>：命令执行时所使用的终端 ID</li><li><code>TIME</code>：进程已经运行的时长</li><li><code>CMD</code>：所执行的命令名称</li></ul><p><code>ps</code> 一个十分常见的用法是 <code>ps aux</code>，可以用来显示系统中运行的所有进程，同时也可以提供更多的信息，比如哪个用户启动了该进程、多少 CPU 和内存被占用了等等。</p><p>把 <code>ps aux</code> 和 <code>grep</code> 命令通过<strong>管道</strong>连接来查找特定的进程也是非常常见的用法，例如如果我们想要看见 SSH 服务 <code>sshd</code> 是否正在运行，我们可以使用下面的命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ ps aux | grep sshd</span><br><span class="line">root      2848  ... /usr/sbin/sshd -D</span><br></pre></td></tr></table></figure><p><code>...</code> 省略了一些信息来来简化上面的输出，其中应该包含了<strong>内存占用</strong>、<strong>运行时间</strong>等等，<code>root</code> 是执行进程的用户，<code>2848</code> 是进程的 ID。</p><h2 id="进程树"><a href="#进程树" class="headerlink" title="进程树"></a>进程树</h2><p>每一个进程都是被别的进程<strong>启动</strong>的，或者说是<strong>复刻</strong>（Fork）的。当系统刚刚启动的时候，有一个非常特别的根进程 <code>init</code> ，它就是是直接被操作系统<strong>内核</strong>启动的。</p><p>这样一来，这个系统中运行的所有进程集合就构成了一颗以 <code>init</code> 进程为根节点的<strong>进程树</strong>，所有的进程都有一个父进程，也有可能有多个子进程。</p><p>比方说，每次我们在 <code>bash</code> 命令行<strong>提示符</strong>下执行一个命令的时候，<code>bash</code> 会<strong>复刻</strong>一个进的进程来执行这个命令，这时这个进程就变成了 <code>bash</code> 的子进程了。</p><p>相似地，当我们看见一个「登录」<strong>提示符</strong>时，这其实是 <code>login</code> 命令在运行着。如果我们成功的登录了，<code>login</code> 命令会<strong>复刻</strong>一个新的进程来执行登录用户选择的 shell。</p><p><code>ps</code> 命令展示了一个扁平化的进程列表，但是我们可以使用 <code>pstree</code> 命令来查看树形结构的进程列表，像下面这样：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">$ pstree</span><br><span class="line">init─┬─atd</span><br><span class="line">     ├─cron</span><br><span class="line">     ├─dbus-daemon</span><br><span class="line">     ├─dhclient</span><br><span class="line">     ├─getty</span><br><span class="line">     ├─mysqld_safe───mysql</span><br><span class="line">     ├─nginx───nginx</span><br><span class="line">     ├─ntpd</span><br><span class="line">     ├─rsyslogd───3*[&#123;rsyslogd&#125;]</span><br><span class="line">     ├─ruby─┬─4*[ruby───&#123;ruby&#125;]</span><br><span class="line">     │      └─&#123;ruby&#125;</span><br><span class="line">     ├─sshd─┬─sshd───bash</span><br><span class="line">     │      └─bash───pstree</span><br><span class="line">     ├─systemd-logind</span><br><span class="line">     ├─systemd-udevd</span><br><span class="line">     ├─upstart-file-br</span><br><span class="line">     ├─upstart-socket-</span><br><span class="line">     └─upstart-udev-br</span><br></pre></td></tr></table></figure><p>我们还可以通过 <code>ps auxf</code> 来查看树形结构的显示，只是效果上可能不那么用户友好罢了。</p><h2 id="进程归属权"><a href="#进程归属权" class="headerlink" title="进程归属权"></a>进程归属权</h2><p>每一个进程都<strong>归属</strong>于某个特定的用户，归属于该用户的进程有权限像该用户的直接登录了一样执行所有该用户可以执行的所有命令。</p><p>比方说，假如有一个进程归 <code>kchen</code> 用户所有，那么这个进程就可以做所有 <code>kchen</code> 用户能做的事情了：编辑 <code>kchen</code> 用户 <code>home</code> 目录下的文件，启动一个归属于 <code>kchen</code> 用户的新进程，等等。</p><p>系统进程比如 <code>init</code> 和 <code>login</code> 归属于 <code>root</code> 用户，而且当一个<strong>根进程</strong>复刻一个新进程的时候，它可以改变这个子进程的归属。</p><p>所以，当我们登录后， <code>login</code> 命令会复刻一个新的进程我运行我们的 shell，但是新的进程是所属于成功登陆的那个用户名的。接下来所有的后续命令都会以该用户的名义执行，所启动的进程都归属于他。</p><p>默认情况下，只有 <code>root</code> 进程可以像这样改变归属权。</p><h2 id="什么是-Init-System"><a href="#什么是-Init-System" class="headerlink" title="什么是 Init System"></a>什么是 Init System</h2><p>操作系统<strong>内核</strong>在<strong>初始化进程</strong>中所做的最后一件事情就是启动「init system」，也就是执行 <code>/sbin/init</code> 命令。「init system」有很多种，但它们都有相同的职责：</p><ol><li>控制哪些<strong>服务</strong>在系统启动时跟随启动</li><li>提供可以开启、停止<strong>服务</strong>的工具，并且给出<strong>服务</strong>的<strong>状态</strong>信息总览</li><li>提供一个可以编写新的服务的框架</li></ol><p>这里的<strong>服务</strong>涵盖了从 web 服务器到用来管理登录的系统级服务器在内的所有服务。基本上，一个「init system」的工作就是让所有<strong>面向用户</strong>（即非内核）的程序和服务运行起来。</p><p>(1)-(3) 中设计的特定命令和工具会因不同的「init system」而各有不同。Linux 系统历史上最通用的一个「init system」叫做「System V Init」，它是以极具影响力的 UNIX SYSTEM V 来命名的。在现在 Linux 系统中，同时被 CentOS、RedHad、Debian、Ubuntu 等等主流发行版本所采用的「init system」叫做「systemd init system」。</p><p>有两点需要铭记：</p><ol><li>不同的 Linux 发行版本可以使用不同的「init system」</li><li>同一 Linux 发型版本的不同版本号可以使用不同的「init system」</li></ol><blockquote><p>例如，Ubuntu 在 <code>v15.04</code> 中开始使用 <code>systemd</code> 作为默认的「init system」。</p></blockquote><h2 id="终止进程"><a href="#终止进程" class="headerlink" title="终止进程"></a>终止进程</h2><p>很多时候，我们会想要<strong>终止</strong>那些占用了过多资源或者<strong>未响应</strong>的进程，两个常见的终止进程的命令是 <code>kill</code> 和 <code>killall</code>。</p><p><code>kill</code> 命令使用<strong>进程号</strong> PID 来终止进程，我们可以通过 <code>ps</code> 命令来获取<strong>进程号</strong>。<code>killall</code> 命令通过给出<strong>进程名</strong>来终止进程，该命令会终止该<strong>进程名</strong>下的所有进程。</p><p>如果我们的 <code>ps</code> 命令有如下输出：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">$ ps</span><br><span class="line">  PID TTY           TIME CMD</span><br><span class="line">20735 ttys000    0:00.10 -bash</span><br><span class="line"> 3086 ttys002    0:00.70 -bash</span><br></pre></td></tr></table></figure><p>现在有两个 <code>bash</code> 进程正在运行，一个的 PID 是 <code>20735</code>，另一个的 PID 是 <code>3086</code>。执行 <code>kill 3086</code> 会促使操作系统发送「graceful shutdown」信号给这个 <code>bash</code> 进程，但是如果执行 <code>killall bash</code> 命令，则会让操作系统终止<strong>所有</strong> <code>bash</code> 进程。</p><h3 id="发送不同的信号"><a href="#发送不同的信号" class="headerlink" title="发送不同的信号"></a>发送不同的信号</h3><p>默认情况下，<code>kill</code> 和 <code>killall</code> 命令会发送 <code>TERM</code> 信号给特定的进程。<code>TERM</code> 信号是一个「优雅」的终止信号，进程收到这个信号时会以合适的方式处理和结束进程。比如，被终止的进程可能想要在终止之前完成当前的任务、或者是清理可能会残留在系统中的临时文件等等。</p><p>如果一个进程有漏洞导致它已经不能响应 <code>TERM</code> 信号了，这种情况下我们就只能发送另一个比较激进的信号了，<code>KILL</code>。有两种方法可以发送这个信号：</p><ol><li><code>kill -KILL 3086</code></li><li><code>kill -9 3086</code></li></ol><p>所有的信号都有一个数字编号，<code>KILL</code> 就是 <code>9</code>。</p><p><code>kill -9</code> 或者 <code>killall -9</code> 指令都是非常激进了，粗略地等同于直接拔掉计算机的电源。像这样来终止进程可能会留下一堆麻烦，只不过如果进程真的不响应了，也没啥别的办法。</p><p>所以，在使用 <code>kill -9 PID</code> 之前，一定要先尽量尝试使用 <code>kill PID</code> 才是。</p><h2 id="使用-top-来查看资源占用"><a href="#使用-top-来查看资源占用" class="headerlink" title="使用 top 来查看资源占用"></a>使用 top 来查看资源占用</h2><p><code>ps</code> 命令会给出一个当前运行进程的<strong>快照</strong>，而 <code>top</code> 命令则是给出一个<strong>持续更新</strong>、<strong>可排序</strong>的进程列表。<code>top</code> 命令常常被用来查看那个进程占用了最多的 CPU 或者内存。</p><p>下面是一些根据内存占用排序的 <code>top</code> 输出，用 <code>...</code> 来做了部分省略：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">PID   USER   ...  %CPU %MEM    TIME+ COMMAND</span><br><span class="line">14944 kchen  ...  0.0  9.5  17:03.55 ruby</span><br><span class="line">14947 kchen  ...  0.0  9.2  17:03.19 ruby</span><br><span class="line">3164  mysql  ...  0.3  2.5  13:31.83 mysqld</span><br></pre></td></tr></table></figure><p>Linux 有这写通用的命令来操作 <code>top</code> 的输出结果：</p><ol><li><code>P</code> （大写）可以按照 CPU 占用来排序</li><li><code>M</code> （大写）可以按照内存占用来排序</li><li><code>&lt;</code> 将排序列左移一栏</li><li><code>&gt;</code> 将排序列右移一栏</li><li><code>q</code> 退出 <code>top</code></li></ol><p>你可以通过阅读 <code>man top</code> 来获取更多关于如何配置 <code>top</code> 的显示，也可以使用 <code>htop</code> 这个程序来实现更为强大的显示和功能效果。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;在 Linux 系统中，特别是没有 GUI 的发行版本里，与进程打交道是一件家常便饭的事情，能不能把进程管理玩溜了，完全可以决定你 Linux 系统的使用体验。今天，我们来看一看常见的进程管理工具和技巧。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Tips" scheme="http://kchen.cc/tags/Tips/"/>
    
      <category term="Linux" scheme="http://kchen.cc/tags/Linux/"/>
    
  </entry>
  
  <entry>
    <title>Java -  对非不可变对象使用拷贝构造器</title>
    <link href="http://kchen.cc/2017/02/22/java-copy-constructor/"/>
    <id>http://kchen.cc/2017/02/22/java-copy-constructor/</id>
    <published>2017-02-22T08:03:18.000Z</published>
    <updated>2021-08-31T01:59:39.004Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>比起使用 <code>clone</code> 方法，在 Java 中拷贝一个对象更加简单的办法是使用<strong>拷贝构造器</strong>。比方说，我们的 <code>Person</code> 类中有如下的构造函数：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Person</span><span class="params">(String name, <span class="keyword">int</span> age)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.name = name;</span><br><span class="line">  <span class="keyword">this</span>.age = age;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>那么就可以有这样的拷贝构造器：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Person</span><span class="params">(Person person)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>(person.getName(), person.getAge());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>该构造器可以使用和被拷贝对象同样的 <code>name</code> 和 <code>age</code> 属性，创建另一个 <code>Person</code> 对象。然而，必须要注意的是，拷贝<strong>不可变对象</strong>是完全没有必要的，所以我们只能用这种拷贝构造器来操作那些状态可变的对象。举例来说，只要 <code>Person</code> 类有这样的方法：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setName</span><span class="params">(String name)</span> </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.name = name;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>那么就大可使用拷贝构造器。</p><blockquote><p>更多资料，请参阅<a href="http://www.javapractices.com/topic/TopicAction.do?Id=71" target="_blank" rel="noopener">相关文档</a>。</p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="Java" scheme="http://kchen.cc/tags/Java/"/>
    
      <category term="Tips" scheme="http://kchen.cc/tags/Tips/"/>
    
  </entry>
  
  <entry>
    <title>Java - 双花括号初始化</title>
    <link href="http://kchen.cc/2017/02/22/java-double-brace-initialization/"/>
    <id>http://kchen.cc/2017/02/22/java-double-brace-initialization/</id>
    <published>2017-02-22T07:54:09.000Z</published>
    <updated>2021-08-31T01:59:39.004Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>相较于手动地<strong>初始化</strong>具有初始元素的 set、list 和 map，Java 提供了另外一种更直接和简便的方法来快速完成：<strong>双花括号初始化</strong>。例如下方的代码：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> Set&lt;String&gt; mySet = <span class="keyword">new</span> HashSet&lt;String&gt;();</span><br><span class="line">mySet.add(<span class="string">"one"</span>);</span><br><span class="line">mySet.add(<span class="string">"two"</span>);</span><br><span class="line">mySet.add(<span class="string">"three"</span>);</span><br><span class="line"></span><br><span class="line">someFunction(mySet);</span><br></pre></td></tr></table></figure><p>就可以利用<strong>双花括号初始化</strong>简化成这样：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">someFunction(<span class="keyword">new</span> HashSet&lt;String&gt;() &#123;&#123;</span><br><span class="line">  add(<span class="string">"one"</span>);</span><br><span class="line">  add(<span class="string">"two"</span>);</span><br><span class="line">  add(<span class="string">"three"</span>);</span><br><span class="line">&#125;&#125;);</span><br></pre></td></tr></table></figure><blockquote><p>更多资料，请查看<a href="http://wiki.c2.com/?DoubleBraceInitialization" target="_blank" rel="noopener">相关文档</a>。</p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="Java" scheme="http://kchen.cc/tags/Java/"/>
    
      <category term="Tips" scheme="http://kchen.cc/tags/Tips/"/>
    
  </entry>
  
  <entry>
    <title>Java - 使用 WeakHashMap</title>
    <link href="http://kchen.cc/2017/02/22/java-weakhashmap/"/>
    <id>http://kchen.cc/2017/02/22/java-weakhashmap/</id>
    <published>2017-02-22T07:12:31.000Z</published>
    <updated>2021-08-31T01:59:39.005Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p><code>WeakHashMap</code> 是一种特别的 <code>Map</code> 实现，它的所有键都存储在 <code>WeakReference</code> 中。相较于 <code>HashMap</code>，<code>WeakHashMap</code> 与其功能基本完全一致，除了唯一一个显著的差别：如果 Java 的内存管理器不再有某个键对象的<strong>强引用</strong>，那么这个<strong>条目</strong>就会被从该 Map 中移除。</p><p>我们这样来创建一个 <code>WeakHashMap</code>：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">HashMap map = <span class="keyword">new</span> WeakHashMap();</span><br></pre></td></tr></table></figure><p>我们可以通过引用某些对象的方式使用 <code>WeakHashMap</code> 来保存资源，但是同时允许这个资源在没有引用的时候被 Java 的<strong>垃圾回收器</strong>回收：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">System.gc();</span><br></pre></td></tr></table></figure><p>所以大家普遍认为，可以通过 <code>WeakHashMap</code> 来解决缓存问题，因为当引用过期之后就会被丢弃。</p><p>这个类的另一种用法是创建 canonical map，你可以存储额外的属性在某个对象里面，因为当它们过期之后，<code>WeakHashMap</code> 的相应条目就会立马被丢弃。</p><blockquote><p>关于 <code>WeakHashMap</code> 的使用例子，请阅读<a href="http://stackoverflow.com/questions/10599710/weakhashmap-example" target="_blank" rel="noopener">参考资料</a>。</p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="Java" scheme="http://kchen.cc/tags/Java/"/>
    
      <category term="Tips" scheme="http://kchen.cc/tags/Tips/"/>
    
  </entry>
  
  <entry>
    <title>Java - 工具类和静态方法</title>
    <link href="http://kchen.cc/2017/02/21/java-utility-classes-and-static-methods/"/>
    <id>http://kchen.cc/2017/02/21/java-utility-classes-and-static-methods/</id>
    <published>2017-02-21T06:12:16.000Z</published>
    <updated>2021-08-31T01:59:39.005Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p><strong>工具类</strong>定义了一组通用的、功能复用的<strong>方法</strong>，它们不依赖于任何的<strong>对象</strong>，所以这些方法应该用 <code>static</code> 关键字声明成<strong>静态的</strong>。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Utility</span> </span>&#123;</span><br><span class="line">  <span class="function"><span class="keyword">private</span> <span class="title">Utility</span><span class="params">()</span> </span>&#123;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="title">multiply</span><span class="params">(<span class="keyword">int</span> a, <span class="keyword">int</span> b)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> a * b;</span><br><span class="line">  &#125;</span><br><span class="line">  ...</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面的例子中，<code>multiply</code> 方法就是静态的，所以我们不必生成任何 <code>Utility</code> 类的<strong>实例</strong>来调用该方法。</p><p>为了防止 <code>Utility</code> 类被调用于<strong>实例化</strong>，我们可以将<strong>构造函数</strong>用 <code>private</code> 关键字定义成<strong>私有的</strong>。</p><blockquote><p>更多关于静态方法的内容，请参阅<a href="http://javarevisited.blogspot.jp/2011/11/static-keyword-method-variable-java.html" target="_blank" rel="noopener">相关文档</a>。</p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="Java" scheme="http://kchen.cc/tags/Java/"/>
    
      <category term="Tips" scheme="http://kchen.cc/tags/Tips/"/>
    
  </entry>
  
  <entry>
    <title>Linux - 用户与权限</title>
    <link href="http://kchen.cc/2017/02/21/linux-users-and-permissions/"/>
    <id>http://kchen.cc/2017/02/21/linux-users-and-permissions/</id>
    <published>2017-02-21T05:19:39.000Z</published>
    <updated>2021-08-31T01:59:39.008Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>使用 Linux 系统，不免会和<strong>用户</strong>和<strong>权限</strong>打交道，本文介绍了<strong>根权限</strong>和文件的<strong>权限属性</strong>等概念和应用。</p><a id="more"></a><h2 id="su-和-sudo"><a href="#su-和-sudo" class="headerlink" title="su 和 sudo"></a>su 和 sudo</h2><p>在 Linux 中，<code>su</code> 命令和 <code>sudo</code> 命令有着十分巨大的区别：</p><ul><li><code>su</code> 命令会把你切换到根用户 <code>root</code></li><li><code>sudo</code> 会使用根权限来执行命令</li></ul><p>简单的说来，<code>sudo</code> 其实就是代表其他授权的用户来执行 <strong>root 命令</strong>的二进制 <strong>setuid</strong>。</p><p>我们可以通过修改（需要 root 权限）下列文件的中的用户列表，来决定哪些用户可以执行 <code>sudo</code> 命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ sudo /usr/sbin/visudo</span><br></pre></td></tr></table></figure><p>默认情况下，这个列表如下所示：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#User privilege specification</span></span><br><span class="line">root ALL=(ALL) ALL</span><br></pre></td></tr></table></figure><p>每一个 <code>sudo</code> 行的语法是：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">user machine=(effective_user) <span class="built_in">command</span></span><br></pre></td></tr></table></figure><p>通过上面的语法，我们可以授予某个用户 <strong>root 权限</strong>，其中每一个域代表：</p><ul><li><code>user</code> 是新的 <code>sudo</code> 用户的用户名</li><li><code>machine</code> 是 <code>sudo</code> 生效的主机名</li><li><code>effective_user</code> 代表被允许执行命令的有效用户</li><li><code>command</code> 代表这这个用户可以执行的一系列命令</li></ul><h2 id="详解-umask"><a href="#详解-umask" class="headerlink" title="详解 umask"></a>详解 umask</h2><p>每一个文件和文件夹在被创建的时候都会被赋予一定的<strong>权限属性</strong>，这些值可以通过 <code>umask</code> 来指定。正如 <code>umask</code> 的名称所显示的那样，这个值本身其实就是一个可以<strong>禁用</strong>相应权限属性的<strong>掩码</strong>。</p><blockquote><p><strong>掩码</strong>表示一个有效的 4 位 8 进制数值。如果把少于 4 位的数值作为参数传入，高位会被用 <code>0</code> 补全。</p></blockquote><p>默认情况下，<strong>文件夹</strong>在被创建的时候能获取的权限属性是 <code>777</code> （<code>rwxrwxrwx</code>），<strong>文件</strong>在被创建的时候能获取的权限属性是 <code>666</code> （<code>rw-rw-rw-</code>），二者的值都可以被被 <code>umask</code> 的掩码给<strong>减掉</strong>。</p><p><strong>掩码过程</strong>相当于禁止掉相应的<strong>权限位</strong>，如果相应位上本身就不具备权限，那么掩码就不会起作用。比如说，如果使用 值为 <code>111</code> 的 <code>umask</code> 来创建一个文件，并不会影响文件的最终权限：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">rw-rw-rw-</span><br><span class="line"><span class="comment"># masking x bit still yields</span></span><br><span class="line">rw-rw-rw-</span><br></pre></td></tr></table></figure><p>但是，如果 <code>umask</code> 的值是 333 的话，情况就不一样了，<code>w</code> 位和 <code>x</code> 位的权限都会被禁止掉：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">rw-rw-rw-</span><br><span class="line"><span class="comment"># masking w and x bits</span></span><br><span class="line">r--r--r--</span><br></pre></td></tr></table></figure><p>我们可以这样来查看当前的 <code>umask</code> 值：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">umask</span></span><br><span class="line">0022</span><br><span class="line"><span class="comment">#these would be the permissions</span></span><br><span class="line"><span class="comment">#for a new file</span></span><br><span class="line">$ touch new-file</span><br><span class="line">$ ls -l new-file</span><br><span class="line">-rw-r--r-- 1 user group 0 new-file</span><br><span class="line"></span><br><span class="line"><span class="comment">#for a new dir</span></span><br><span class="line">$ mkdir new-dir</span><br><span class="line">$ ls -l new-dir</span><br><span class="line">drwxr-xr-x 2 user group 4096 ./</span><br></pre></td></tr></table></figure><p>如果我们要把当前<strong>会话</strong>中的 <code>umask</code> 设定为 <code>077</code> 的话，可以执行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">umask</span> 077</span><br><span class="line"><span class="comment">#or</span></span><br><span class="line">$ <span class="built_in">umask</span> u+rwx,g-rwx,o-rwx</span><br><span class="line"><span class="comment">#or</span></span><br><span class="line">$ <span class="built_in">umask</span> u=rwx,g=,o=</span><br><span class="line"></span><br><span class="line"><span class="comment"># + enables specified permissions</span></span><br><span class="line"><span class="comment"># - disables specified permissions</span></span><br><span class="line"><span class="comment"># = enables specified,disables the others</span></span><br><span class="line"></span><br><span class="line">$ <span class="built_in">umask</span></span><br><span class="line">0077</span><br></pre></td></tr></table></figure><p>如果想要系统上的所有用户或者指定用户都使用设定的 <code>umask</code> 值的话，我们需要把相应的设定写入 <code>/etc/profile</code> 或者指定的 <code>~/.bashrc</code> 文件中去。</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;使用 Linux 系统，不免会和&lt;strong&gt;用户&lt;/strong&gt;和&lt;strong&gt;权限&lt;/strong&gt;打交道，本文介绍了&lt;strong&gt;根权限&lt;/strong&gt;和文件的&lt;strong&gt;权限属性&lt;/strong&gt;等概念和应用。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Tips" scheme="http://kchen.cc/tags/Tips/"/>
    
      <category term="Linux" scheme="http://kchen.cc/tags/Linux/"/>
    
  </entry>
  
  <entry>
    <title>Python - 字典的视图对象和映射类型</title>
    <link href="http://kchen.cc/2017/02/21/python-dinctionary-view-and-map-type/"/>
    <id>http://kchen.cc/2017/02/21/python-dinctionary-view-and-map-type/</id>
    <published>2017-02-21T02:49:10.000Z</published>
    <updated>2021-08-31T01:59:39.010Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p><strong>字典</strong>是 Python 中非常常用的一种<strong>集合</strong>类型，今天我们来看一下关于字典的<strong>视图对象</strong>和<strong>映射类型</strong>。</p><a id="more"></a><h2 id="字典的视图对象"><a href="#字典的视图对象" class="headerlink" title="字典的视图对象"></a>字典的视图对象</h2><p>Python 的字典提供以下三种方法来获取<strong>视图对象</strong>：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">dict.keys() </span><br><span class="line"><span class="comment"># returns a new view of dictionary's keys</span></span><br><span class="line">dict.values()</span><br><span class="line"><span class="comment"># returns a new view of dictionary's values</span></span><br><span class="line">dict.items()</span><br><span class="line"><span class="comment"># returns a new view of dictionary's items</span></span><br><span class="line"><span class="comment"># items are (key, value) pairs</span></span><br></pre></td></tr></table></figure><p><strong>视图对象</strong>将会动态地与字典的<strong>条目</strong>绑定，也就是说，当字典发生变化的时候，他的视图对象也会相应地发生变化。</p><p>我们可以通过 <code>len(dictview)</code> 方法来获取字典中条目的数量：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>a = &#123;<span class="string">'one'</span>:<span class="number">1</span>, <span class="string">'two'</span>:<span class="number">2</span>&#125;</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>keys = a.keys()</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>len(keys)</span><br><span class="line"><span class="number">2</span></span><br></pre></td></tr></table></figure><p>字典的 <code>iter()</code> 方法对字典的视图也行之有效，其会返回基于<strong>键</strong>、<strong>值</strong>或者<strong>键值对</strong>的<strong>迭代器</strong>：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">iter(keys)</span><br><span class="line"><span class="comment"># iterator over keys is returned</span></span><br></pre></td></tr></table></figure><p>同样的，使用 <code>in</code> 关键词来判定集合的成员也可以同时用在字典或者字典的视图上：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="string">'one'</span> <span class="keyword">in</span> keys</span><br><span class="line"><span class="literal">True</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="string">'three'</span> <span class="keyword">in</span> keys</span><br><span class="line"><span class="literal">False</span></span><br></pre></td></tr></table></figure><blockquote><p>更多关于字典的视图对象，请参阅<a href="https://docs.python.org/3.5/library/stdtypes.html#dictionary-view-objects" target="_blank" rel="noopener">官方文档</a>。</p></blockquote><h2 id="字典的标准映射类型"><a href="#字典的标准映射类型" class="headerlink" title="字典的标准映射类型"></a>字典的标准映射类型</h2><p><strong>字典</strong>（<code>dict</code>）是 Python 的主要<strong>映射类型</strong>，它将<strong>可哈希化</strong>（hashable）的值映射为任意的对象。Python 中的字典和 Perl 语言、Ruby 语言中的哈希，C 语言中的哈希表是相似的。</p><p>我们可以使用 <code>{key: value}</code> 语法，<code>dict</code> 构造器或者会返回<strong>元组</strong>的 <code>zip</code> 方法来生成一个<strong>字典</strong>：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">d1 =&#123;<span class="string">'first'</span>:<span class="number">1</span>, <span class="string">'second'</span>:<span class="number">2</span>&#125;</span><br><span class="line">d2 = dict(first=<span class="number">1</span>, second=<span class="number">2</span>)</span><br><span class="line">d3 = dict(zip([<span class="string">'first'</span>,<span class="string">'second'</span>], [<span class="number">1</span>,<span class="number">2</span>]))</span><br><span class="line">d4 = dict(&#123;<span class="string">'second'</span>:<span class="number">2</span>, <span class="string">'first'</span>:<span class="number">1</span>&#125;)</span><br></pre></td></tr></table></figure><p>在<strong>字典</strong>上获取和使用一个<strong>键</strong>的<strong>迭代器</strong>：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>it = iter(d1)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>type(it)</span><br><span class="line">&lt;<span class="class"><span class="keyword">class</span> '<span class="title">dict_keyiterator</span>' <span class="title">at</span></span></span><br><span class="line"><span class="class">0<span class="title">x000000005801A420</span>&gt;</span></span><br><span class="line"><span class="class">&gt;&gt;&gt; <span class="title">for</span> <span class="title">k</span> <span class="title">in</span> <span class="title">it</span>:</span> print(k)</span><br><span class="line">...</span><br><span class="line">first</span><br><span class="line">second</span><br></pre></td></tr></table></figure><p>用字典来更新另一个字典：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span>prefs = &#123;<span class="string">"fruit"</span>: <span class="string">"apple"</span>, <span class="string">"car"</span>: <span class="string">"Ford"</span>&#125;</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>prefs</span><br><span class="line">&#123;<span class="string">'car'</span>: <span class="string">'Ford'</span>, <span class="string">'fruit'</span>: <span class="string">'apple'</span>&#125;</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>prefs2 = &#123;<span class="string">"fruit"</span>: <span class="string">"orange"</span>&#125;</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>prefs.update(prefs2)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>prefs</span><br><span class="line">&#123;<span class="string">'car'</span>: <span class="string">'Ford'</span>, <span class="string">'fruit'</span>: <span class="string">'orange'</span>&#125;</span><br></pre></td></tr></table></figure><p>还可以用一个旧的字典（<code>seq</code>）来构造一个新的字典，让新的字典和 <code>seq</code> 有相同的键：</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">seq = (<span class="string">'one'</span>, <span class="string">'two'</span>)</span><br><span class="line">dict = dict.fromkeys(seq)</span><br><span class="line"><span class="comment"># dict is &#123;'one': None, 'two': None &#125;</span></span><br><span class="line">dict = dict.fromkeys(seq,<span class="number">10</span>)</span><br><span class="line"><span class="comment"># dict is  &#123;'one: 10, 'two': 10 &#125;</span></span><br></pre></td></tr></table></figure><blockquote><p>更多关于字典的标准映射类型，请参阅<a href="https://docs.python.org/3.5/library/stdtypes.html#mapping-types-dict" target="_blank" rel="noopener">官方文档</a>。</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;strong&gt;字典&lt;/strong&gt;是 Python 中非常常用的一种&lt;strong&gt;集合&lt;/strong&gt;类型，今天我们来看一下关于字典的&lt;strong&gt;视图对象&lt;/strong&gt;和&lt;strong&gt;映射类型&lt;/strong&gt;。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Tips" scheme="http://kchen.cc/tags/Tips/"/>
    
      <category term="Python" scheme="http://kchen.cc/tags/Python/"/>
    
  </entry>
  
  <entry>
    <title>Linux - Iptable 的使用</title>
    <link href="http://kchen.cc/2017/02/20/linux-iptables/"/>
    <id>http://kchen.cc/2017/02/20/linux-iptables/</id>
    <published>2017-02-20T04:39:36.000Z</published>
    <updated>2021-08-31T01:59:39.006Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p><code>ipatable</code> 是 Linux 内核自带的防火墙解决方案。它通过对经由网络的每一个包进行一系列的规则验证来决定该如何处理。</p><p>我们可以匹配<strong>协议类型</strong>、<strong>源地址</strong>和<strong>目的地址</strong>或者<strong>端口</strong>、接口以及它们和之前的包的关系等等。这些规则会被进一步的结构化成为<strong>规则链</strong>，网络包将会顺序地被规则链中的规则一一检查。它们可以被按需生成，但是有3种默认的规则：</p><ul><li><code>INPUT</code>：处理流入服务器的流量。</li><li><code>OUTPUT</code>：处理流出服务的流量。</li><li><code>FORWARD</code>：处理流经服务器的流量而不停止它们。</li></ul><a id="more"></a><p>列出当前的规则：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ iptables -L -n</span><br></pre></td></tr></table></figure><p>列出当前的 <code>NAT</code> 规则：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ iptables -L -n -t nat</span><br></pre></td></tr></table></figure><p>阻断指定 IP 的流量：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ iptables -I INPUT -s 10.10.10.10 -j DROP</span><br></pre></td></tr></table></figure><p>允许指定 IP 的流量：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ iptables -I INPUT -s 10.10.10.10 -j ACCEPT</span><br></pre></td></tr></table></figure><p>允许指定端口的流量：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ iptables -I INPUT -p tcp -m tcp --dport 31415 -j ACCEPT</span><br></pre></td></tr></table></figure><p><code>-I</code> 和 <code>-A</code> 参数的区别在于 <code>-I</code> 将会把指定的规则插入到规则链的最开始来抑制 <code>-A</code> 所增加的附带规则。</p><p>我们可以通过以下命令来检查当前路由表的防火墙状态：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ sudo iptables -L -n -v</span><br></pre></td></tr></table></figure><p>其中 <code>-L</code> 列出规则，<code>-v</code> 显示详细信息，我们通过 <code>-n</code> 标识禁止 DNS 方案来提高列出规则的速度。我们可以看到类似以下的输出：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">Chain INPUT (policy ACCEPT 0 packets, 0 bytes)</span><br><span class="line"> pkts bytes target     prot opt <span class="keyword">in</span>     out     <span class="built_in">source</span>               destination</span><br><span class="line">  10   15    DROP       all --   *      *    192.64.174.69           0.0.0.0/0</span><br><span class="line"></span><br><span class="line">Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)</span><br><span class="line"> pkts bytes target     prot opt <span class="keyword">in</span>     out     <span class="built_in">source</span>               destination</span><br><span class="line"></span><br><span class="line">Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)</span><br><span class="line"> pkts bytes target     prot opt <span class="keyword">in</span>     out     <span class="built_in">source</span>               destination</span><br></pre></td></tr></table></figure><p>其中有一条激活的 <code>INPUT</code> 规则，其阻止了来自 192.64.174.69 的所有流量。</p><blockquote><p>更多有关 <code>ipatable</code> 的使用，请参阅<a href="https://www.cyberciti.biz/faq/rhel-fedorta-linux-iptables-firewall-configuration-tutorial/" target="_blank" rel="noopener">相关资料</a>。</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;code&gt;ipatable&lt;/code&gt; 是 Linux 内核自带的防火墙解决方案。它通过对经由网络的每一个包进行一系列的规则验证来决定该如何处理。&lt;/p&gt;
&lt;p&gt;我们可以匹配&lt;strong&gt;协议类型&lt;/strong&gt;、&lt;strong&gt;源地址&lt;/strong&gt;和&lt;strong&gt;目的地址&lt;/strong&gt;或者&lt;strong&gt;端口&lt;/strong&gt;、接口以及它们和之前的包的关系等等。这些规则会被进一步的结构化成为&lt;strong&gt;规则链&lt;/strong&gt;，网络包将会顺序地被规则链中的规则一一检查。它们可以被按需生成，但是有3种默认的规则：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;INPUT&lt;/code&gt;：处理流入服务器的流量。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;OUTPUT&lt;/code&gt;：处理流出服务的流量。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FORWARD&lt;/code&gt;：处理流经服务器的流量而不停止它们。&lt;/li&gt;
&lt;/ul&gt;
    
    </summary>
    
    
      <category term="Tips" scheme="http://kchen.cc/tags/Tips/"/>
    
      <category term="Linux" scheme="http://kchen.cc/tags/Linux/"/>
    
  </entry>
  
  <entry>
    <title>Java - 在泛型中使用受限类型参数</title>
    <link href="http://kchen.cc/2017/02/19/java-tips-using-bounded-type-parameters-in-generic-method/"/>
    <id>http://kchen.cc/2017/02/19/java-tips-using-bounded-type-parameters-in-generic-method/</id>
    <published>2017-02-19T09:19:14.000Z</published>
    <updated>2021-08-31T01:59:39.005Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>许多时候在 Java 中使用<strong>泛型</strong>是非常方便且必要的，但是我们不可能接受所有类型的参数来确保泛型的每一个功能都能得到维护。</p><p>为了解决这个问题，我们可以使用<strong>受限类型参数</strong>来限制泛型只能接受特定类型的实参。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> &lt;T extends Shape&gt; </span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">drawAll</span><span class="params">(List&lt;T&gt; shapes)</span></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (Shape s: shapes) &#123;</span><br><span class="line">        s.draw(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面这个方法是用来绘制一组形状（<code>Shape</code>）的，如果我们使用<strong>不受限类型参数</strong>的泛型来实现这个方法的话，有可能导致的问题是其他类型可能并不能并不具备 <code>draw()</code> 方法。</p><p>通过指定 <code>&lt;T extends Shape&gt;</code> 我们可以确保只有 <code>Shape</code> 的子类可以被传递到这个方法中来。</p><blockquote><p>关于这一部分的更多内容，详见<a href="https://docs.oracle.com/javase/tutorial/java/generics/bounded.html" target="_blank" rel="noopener">官方文档</a>。</p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="Java" scheme="http://kchen.cc/tags/Java/"/>
    
      <category term="Tips" scheme="http://kchen.cc/tags/Tips/"/>
    
  </entry>
  
  <entry>
    <title>Java - 通过 Default 更新接口类方法</title>
    <link href="http://kchen.cc/2017/02/19/java-tips-updating-interface/"/>
    <id>http://kchen.cc/2017/02/19/java-tips-updating-interface/</id>
    <published>2017-02-19T08:57:50.000Z</published>
    <updated>2021-08-31T01:59:39.004Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>假设有下列接口：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Cooking</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">fry</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">boil</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">chop</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>为了添加新的功能，直接向 <code>Cooking</code> 接口类中增加 <code>microwave()</code> 方法将会造成不少的麻烦。任何实现了 <code>Cooking</code> 接口的类现在都必须要同步更新实现这个方法，以确保整个类重新生效、正常工作。</p><p>为了避免这种情况，我们可以给 <code>microwave()</code> 方法一个默认 <code>default</code> 实现：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Cooking</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">fry</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">boil</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">chop</span><span class="params">()</span></span>;</span><br><span class="line">    <span class="function"><span class="keyword">default</span> <span class="keyword">void</span> <span class="title">microwave</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">//some code implementing microwave</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>因为 <code>microwave()</code> 方法已经在 <code>Cooking</code> 接口类中有了默认实现的定义，所有实现了该接口的类都不需要额外实现 <code>microwave()</code> 方法来确保其正常工作了。</p><p>这个方法在我们不破坏现有代码的情况下，想要增加一些新的功能时，十分的好用。</p><blockquote><p>Note：该方法需要 Java 8 以上的支持。详见<a href="https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html" target="_blank" rel="noopener">官方文档</a>。</p></blockquote>]]></content>
    
    <summary type="html">
    
      
      
        &lt;link rel=&quot;stylesheet&quot; class=&quot;aplayer-secondary-style-marker&quot; href=&quot;/assets/css/APlayer.min.css&quot;&gt;&lt;script src=&quot;/assets/js/APlayer.min.js&quot; cla
      
    
    </summary>
    
    
      <category term="Java" scheme="http://kchen.cc/tags/Java/"/>
    
      <category term="Tips" scheme="http://kchen.cc/tags/Tips/"/>
    
  </entry>
  
  <entry>
    <title>Linux - Cat 命令的实用技巧</title>
    <link href="http://kchen.cc/2017/02/02/tip-cat/"/>
    <id>http://kchen.cc/2017/02/02/tip-cat/</id>
    <published>2017-02-02T02:58:22.000Z</published>
    <updated>2021-08-31T01:59:39.011Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>GNU 文本程序实用技巧系列之——<code>cat</code>。<code>cat</code> 这个命令是 UNIX 爱好者所热爱的，也是厌恶 UNIX 的人所憎恶的。</p><blockquote><p>The <code>cat</code> utility reads files sequentially, writing them to the standard output.</p></blockquote><a id="more"></a><h2 id="使用-cat-合并多个文件"><a href="#使用-cat-合并多个文件" class="headerlink" title="使用 cat 合并多个文件"></a>使用 cat 合并多个文件</h2><p>有时候，我们常常需要将几个文件处理成一个文件，并将处理的结果保存到一个单独的输出文件中。<code>cat</code> （「concatenate」的缩写）命令可以在其输入上接受一个或多个文件并将它们打印到一个单独的文件中。例如， <code>cat chapter01 chapter02 chapter03 &gt; book</code> 将三个 <code>chapterXX</code> 文件保存在一个单独的 <code>book</code> 文件中。</p><p>输入文件按照它们在 <code>cat</code> 命令后的排列顺序被打印，因此，要调换输出文件中内容的顺序，就必须先调换输入文件的顺序。此外，当需要处理的文件数目过大而无法手工输入这些文件的名称时，可以使用通配符来匹配文件名，例如， <code>cat chapter* &gt; book</code> 可以将当前目录下所有以 <code>chapter</code> 开头的文件全部合并输出到 <code>book</code> 文件中，记住，<strong>文件名将会按升序排列</strong>。这就意味着你会发现 <code>chapter13</code> 被发送到输出中时会在 <code>chapter2</code> 之前，<code>chapter02</code> 之后。有时候，这会引起一些很有意思的问题。</p><p>当 <code>cat</code> 的输出没有被重定向到一个文件或另一个命令的标准输入时，<code>cat</code> 表现出来的行为与多数命令行工具一样，即将其输出发送到控制台。 这意味着你可以使用 <code>cat</code> 来显示文件（也就是 <code>cat</code> 命令最常见的实用方法）；例如，你可以使用 <code>cat /etc/passwd</code> 来显示系统密码文件的内容。为方便起见，你应该用 <code>less</code> 或者 <code>more</code> 来查看大文件，如 <code>less /etc/passwd</code>。（你可以通过输入 <code>man less</code> 学习更多关于 <code>less</code> 的知识）。</p><p>尽管 <code>cat</code> 主要用于合并文件，我们还可以将它用于输入的简单自动处理。例如，你可以使用 一个单独的空白行来除去多行空白行（使用 <code>-s</code> 选项），这是一个在你将源代码公诸于世前进行代码整理工作的好办法。遗憾的是，<code>cat</code> 并没有用于一次清除所有空白行的选项。但这并不是什么大问题，因为你可以方便地使用 <code>sed</code> 命令将这些空白行除去：</p><h2 id="使用-sed-与-cat-除去空白行"><a href="#使用-sed-与-cat-除去空白行" class="headerlink" title="使用 sed 与 cat 除去空白行"></a>使用 sed 与 cat 除去空白行</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">$ cat -s /etc/X11/XF86Config | sed <span class="string">'/^[[:space:]]*$/d'</span></span><br><span class="line">...</span><br><span class="line"><span class="comment"># Multiple FontPath entries are allowed (they are concatenated together)</span></span><br><span class="line"><span class="comment"># By default, Red Hat 6.0 and later now use a font server independent of</span></span><br><span class="line"><span class="comment"># the X server to render fonts.</span></span><br><span class="line">    FontPath   <span class="string">"/usr/X11R6/lib/X11/fonts/TrueType"</span></span><br><span class="line">    FontPath   <span class="string">"unix/:7100"</span></span><br><span class="line">EndSection</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>对于读取配置文件和 HTML 页面的源文件，特别是那些由脚本生成的插入了不必要新行的源文件，以及那些包含大型条件结构（其各个项之间已经用空行分开）的源文件来说，空白行紧缩是一个非常实用的技巧。</p><p><code>cat</code> 的另外一个重要的功能是它可以对行进行编号。这种功能对于程序文档的编制以及法律和科学文档的编制很方便。打印在左边的行号使得参考文档的某一部分变得容易。这在编程、科学研究、业务报告或甚至是立法工作中都是非常重要的。 对行进行编号功能有两个选项： <code>-b</code> 选项（只能对非空白行进行编号）和 <code>-n</code> 选项（可以对所有行进行编号）：</p><h2 id="使用-cat-对行进行编号"><a href="#使用-cat-对行进行编号" class="headerlink" title="使用 cat 对行进行编号"></a>使用 cat 对行进行编号</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">$ cat -b /etc/X11/XF86Config</span><br><span class="line">...</span><br><span class="line">    14  <span class="comment"># Multiple FontPath entries are allowed (they are concatenated together)</span></span><br><span class="line">    15  <span class="comment"># By default, Red Hat 6.0 and later now use a font server independent of</span></span><br><span class="line">    16  <span class="comment"># the X server to render fonts.</span></span><br><span class="line">    17      FontPath   <span class="string">"/usr/X11R6/lib/X11/fonts/TrueType"</span></span><br><span class="line">    18      FontPath   <span class="string">"unix/:7100"</span></span><br><span class="line">    19  EndSection</span><br><span class="line">...</span><br><span class="line">$ cat -n /etc/X11/XF86Config</span><br><span class="line">...</span><br><span class="line">    20  <span class="comment"># Multiple FontPath entries are allowed (they are concatenated together)</span></span><br><span class="line">    21  <span class="comment"># By default, Red Hat 6.0 and later now use a font server independent of</span></span><br><span class="line">    22  <span class="comment"># the X server to render fonts.</span></span><br><span class="line">    23  </span><br><span class="line">    24      FontPath   <span class="string">"/usr/X11R6/lib/X11/fonts/TrueType"</span></span><br><span class="line">    25      FontPath   <span class="string">"unix/:7100"</span></span><br><span class="line">    26  </span><br><span class="line">    27  EndSection</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p><code>cat</code> 还可以在你查看包含如<em>制表符</em>这样的<strong>非打印字符</strong>的文件时起帮助作用。你可以用以下选项来显示<em>制表符</em>：</p><ul><li><code>-T</code> 将<em>制表符</em>显示为 <code>^I</code></li><li><code>-v</code> 显示非打印字符，除了<em>换行符</em>和<em>制表符</em>，它们使用各自效果相当的「控制序列」。 例如，当你处理一个在 Windows 系统中生成的文件时，这个文件将使用 <code>Control-M</code>（ <code>^M</code> ）来标记行的结束。对于代码大于 127 的字符，它们的前面将会被加上 <code>M-</code> （表示「meta」），这与其它系统中在字符前面加上 <code>Alt-</code> 相当。</li><li><code>-E</code> 在每一行的结束处添加<em>美元符</em>（ <code>$</code> ）。</li></ul><h2 id="使用-cat-显示非打印字符"><a href="#使用-cat-显示非打印字符" class="headerlink" title="使用 cat 显示非打印字符"></a>使用 cat 显示非打印字符</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line">$ cat -t /etc/X11/XF86Config</span><br><span class="line">...</span><br><span class="line"><span class="comment"># Multiple FontPath entries are allowed (they are concatenated together)</span></span><br><span class="line"><span class="comment"># By default, Red Hat 6.0 and later now use a font server independent of</span></span><br><span class="line"><span class="comment"># the X server to render fonts.</span></span><br><span class="line">^IFontPath^I<span class="string">"/usr/X11R6/lib/X11/fonts/TrueType"</span></span><br><span class="line">^IFontPath^I<span class="string">"unix/:7100"</span></span><br><span class="line">EndSection</span><br><span class="line">...</span><br><span class="line">$ cat -E /etc/X11/XF86Config</span><br><span class="line">...</span><br><span class="line"><span class="comment"># Multiple FontPath entries are allowed (they are concatenated together)$</span></span><br><span class="line"><span class="comment"># By default, Red Hat 6.0 and later now use a font server independent of$</span></span><br><span class="line"><span class="comment"># the X server to render fonts.$</span></span><br><span class="line">$</span><br><span class="line">    FontPath   <span class="string">"/usr/X11R6/lib/X11/fonts/TrueType"</span>$</span><br><span class="line">    FontPath   <span class="string">"unix/:7100"</span>$</span><br><span class="line">$</span><br><span class="line">EndSection$</span><br><span class="line">...</span><br><span class="line">$ cat -v /etc/X11/XF86Config</span><br><span class="line">...</span><br><span class="line">^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@M-|M-8^X^@^@^@</span><br><span class="line">P^@^O<span class="string">"M-X^O M-@^M^@^@^@M-^@^O"</span>M-@M-k^@M-8*^@</span><br><span class="line">@M-^H<span class="variable">$M</span>-@M-9|A(M-@)M-yM-|M-sM-*M-hW^A^@^@j^@</span><br><span class="line">M-|M-sM-%1M-@M-9^@^B^@^@M-sM-+fM-^A= ^@ ^@</span><br><span class="line">F^@^@   ^@M-9^@^H^@^@M-sM-<span class="variable">$M</span>-G^E(l!M-@M-^?</span><br><span class="line">^IM-A5^@^@^D^@PM-^]M-^\X1M-H%^@^@^D^@tyM-G</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;GNU 文本程序实用技巧系列之——&lt;code&gt;cat&lt;/code&gt;。&lt;code&gt;cat&lt;/code&gt; 这个命令是 UNIX 爱好者所热爱的，也是厌恶 UNIX 的人所憎恶的。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;cat&lt;/code&gt; utility reads files sequentially, writing them to the standard output.&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="Tips" scheme="http://kchen.cc/tags/Tips/"/>
    
      <category term="Linux" scheme="http://kchen.cc/tags/Linux/"/>
    
  </entry>
  
  <entry>
    <title>Github 小技巧</title>
    <link href="http://kchen.cc/2016/12/12/github-cheats/"/>
    <id>http://kchen.cc/2016/12/12/github-cheats/</id>
    <published>2016-12-12T07:49:06.000Z</published>
    <updated>2021-08-31T01:59:39.002Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>Github 中有许多小技巧，今天给大家介绍两个常用的：如何在网页端新建文件夹和隐藏的快捷键操作。同时，介绍一款很实用的项目收藏查看软件给大家。</p><a id="more"></a><h2 id="在-Github-中新建文件夹"><a href="#在-Github-中新建文件夹" class="headerlink" title="在 Github 中新建文件夹"></a>在 Github 中新建文件夹</h2><p>最近，常有朋友问我，如何在 Github  网页端里新建文件夹？确实，Github 网页端并没有明显的提示如何可以直接添加文件夹，只是有一个新建文件的按钮，但这并不代表这一操作没法在网页端完成，实际上你只需要在新建文件名时使用 <code>/</code> 来表示文件夹即可。</p><p><img src="https://thumbs.gfycat.com/HardWideeyedHyrax-size_restricted.gif" alt></p><h2 id="Github-网页端的快捷键操作"><a href="#Github-网页端的快捷键操作" class="headerlink" title="Github 网页端的快捷键操作"></a>Github 网页端的快捷键操作</h2><p>其实 Github 网页端提供了非常全面的快捷键操作，只不过提示也是隐藏得颇深。通过敲击 <code>？</code> 键即可查看完整的快捷键列表。</p><p><img src="http://data.kchen.cc/mac_qrsync/34b8e85ac70dc94abc03e22a1135183b.png-960.jpg" alt></p><p>其中最常用的像是 <code>gc</code> 快速回到 Code View，还有 <code>t</code> 打开文件列表(打开后继续键入字母可以模糊匹配文件名)，以及 <code>s</code> 快速跳转到顶部的搜索框等等，都很实用。</p><h2 id="Star-Order"><a href="#Star-Order" class="headerlink" title="Star Order"></a>Star Order</h2><p>最后给大家推荐一个 Mac 端的小软件——Star Order，它是一款支持 macOS/iOS 双平台的 Github Star 管理软件，面对日积月累的 Star 项目，你需要更灵活的标签管理方式或者更全面准确的排序检索，这款软件可以让你在面对众多 Star 仓库时，临危不乱，迅速找出目标项目，算是 Github 使用的必备伴侣。同类的另一个软件叫做 OhMyStar。</p><p><img src="http://data.kchen.cc/mac_qrsync/a1f09cedc74ba03f0556a471aed5d076.png-960.jpg" alt></p><blockquote><p>其他别的什么小技巧，我想到了再补充吧。</p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;Github 中有许多小技巧，今天给大家介绍两个常用的：如何在网页端新建文件夹和隐藏的快捷键操作。同时，介绍一款很实用的项目收藏查看软件给大家。&lt;/p&gt;
    
    </summary>
    
    
      <category term="Github" scheme="http://kchen.cc/tags/Github/"/>
    
  </entry>
  
  <entry>
    <title>AWS EC 主机无法通过 PHP 连接 RDS MySQL</title>
    <link href="http://kchen.cc/2016/12/02/aws-php-mysql/"/>
    <id>http://kchen.cc/2016/12/02/aws-php-mysql/</id>
    <published>2016-12-02T01:24:07.000Z</published>
    <updated>2021-08-31T01:59:38.999Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><h2 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h2><blockquote><p>当我们选择 AWS EC 作为服务器主机，而 AWS RDS 作为 MySQL 服务器时，如果通过 PHP 访问 MySQL 可能会出现 「Error establishing a database connection」错误，这是使用 WordPress 建站时常见的数据库连接错误。</p></blockquote> <a id="more"></a><h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h2><p> 一、确认自己的安全组权限</p><p> 在 RDS 中需要为自己的数据库配置安全组权限，也即 <code>3306</code> 端口需要向 EC 主机开放：</p><p><img src="http://data.kchen.cc/mac:rt67ujbt678ijbt78ijbty.png-960.jpg" alt="SG"></p><p>其中，选择类型为 <code>MYSQL/Aurora</code>，这样可以开启 <code>3306</code> 端口，把来源定义为我的 IP 以及 EC 主机所在安全组。（只需要键入主机名，AWS 可以自动适配）</p><p>二、关闭安全增强式 Linux（SELinux）</p><p>AWS EC 默认开启了 SELinux 是使得 PHP 服务器不能访问外部 MySQL 的元凶。使用 <code>root</code> 用户修改 EC 主机 <code>/etc/selinux/config</code> 文件中：</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SELinux = disabled</span><br></pre></td></tr></table></figure><p>重启 EC 主机，重新开启 httpd 服务即可。</p>]]></content>
    
    <summary type="html">
    
      &lt;h2 id=&quot;问题描述&quot;&gt;&lt;a href=&quot;#问题描述&quot; class=&quot;headerlink&quot; title=&quot;问题描述&quot;&gt;&lt;/a&gt;问题描述&lt;/h2&gt;&lt;blockquote&gt;
&lt;p&gt;当我们选择 AWS EC 作为服务器主机，而 AWS RDS 作为 MySQL 服务器时，如果通过 PHP 访问 MySQL 可能会出现 「Error establishing a database connection」错误，这是使用 WordPress 建站时常见的数据库连接错误。&lt;/p&gt;
&lt;/blockquote&gt;
    
    </summary>
    
    
      <category term="AWS" scheme="http://kchen.cc/tags/AWS/"/>
    
      <category term="PHP" scheme="http://kchen.cc/tags/PHP/"/>
    
      <category term="RDS" scheme="http://kchen.cc/tags/RDS/"/>
    
      <category term="MySQL" scheme="http://kchen.cc/tags/MySQL/"/>
    
      <category term="WordPress" scheme="http://kchen.cc/tags/WordPress/"/>
    
  </entry>
  
  <entry>
    <title>为你量身定制的终端复用器 K-Tmux</title>
    <link href="http://kchen.cc/2016/11/18/custom-multiplexer-k-tmux/"/>
    <id>http://kchen.cc/2016/11/18/custom-multiplexer-k-tmux/</id>
    <published>2016-11-18T02:03:42.000Z</published>
    <updated>2018-11-04T16:22:33.000Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p>本文是项目 k-tmux 的说明文档，介绍了一套 tmux 的配置。本配置受到 @wlken 的启发。</p><p>ChangeLog: 2018-10-19 更新了弃用的配置命令，完善对鼠标的支持，修改了关闭窗口的键位绑定，安装并使用插件，增加对 prefix 状态和 buffer 复制状态的显示。</p><a id="more"></a><h2 id="什么是-tmux"><a href="#什么是-tmux" class="headerlink" title="什么是 tmux"></a>什么是 tmux</h2><p>简单点说，tmux 是个终端复用器，允许你在一个终端中同时开启多个会话。</p><p><img src="https://camo.githubusercontent.com/d559d2e46c5484e8c978e75a0c2823c554312d72/687474703a2f2f692e696d6775722e636f6d2f4d6b54415a4a702e706e67" alt="display"></p><p>为什么要使用 tmux 呢？</p><p>通俗一点说，通常情况下，每次我们打开一个终端窗口的时候，其实我们是创建了一个和操作系统内核连接的会话。会话可以是本地的，也可以是通过 ssh 连接的。如果是使用 ssh 连接的，一旦连接中断了（网络问题、或者客户端不小心关掉了），那么整个会话也就断了，在会话中执行的命令和运行的程序都会中止。</p><p>使用 tmux 就不一样了。tmux 首先会创建一个 server 进程，你可以在 server 进程下创建会话（session），这里的会话有别于普通的会话，它是托管在 server 进程下的，只要 server 进程不被中止，session 不会因为意外中止（例如和 ssh 客户端断开连接）。任何时候你可以通过 <code>attach</code> 来接入这个会话。</p><p>下面我们看两个简单的场景：</p><p>你在公司/实验室开启了一个会话连接到本地或者 ssh 到某个服务器运行一个程序。程序持续运行着，还没有出结果，你得下班/放学回家了，放心的合上电脑走人即可。回到家，ssh 连接上服务器，<code>attach</code> 到之前的会话，你现在可以看结果了。</p><p>或者：</p><p>你连上某个服务器，打开 tmux，开启了一些需要后台长期执行的服务，然后关闭终端，走人。不用担心自己的服务进程因为关闭终端而中止。</p><h2 id="什么是终端复用器"><a href="#什么是终端复用器" class="headerlink" title="什么是终端复用器"></a>什么是终端复用器</h2><p>那为什么说 tmux 是终端复用器？</p><p>我们来看看 tmux 的层次结构：</p><p><img src="http://ww1.sinaimg.cn/mw690/7178f37egw1esoxc7hp5oj20gm0bkjs6.jpg" alt="descpription"></p><p>一个 tmux server 进程下可以托管多个会话（session），一个会话下面可以开启多个窗口（windows）——窗口就像正常终端中的 tab 标签页一样，一个窗口可以切割成多个窗格（pane）——其实窗格才是正常终端中的会话。</p><p>这样，我们就可以看出，tmux 在多个层级上做了复用：</p><ul><li>我们可以通过不同的会话来分组不同的事务，如果几个人共享一个会话，那么彼此的终端界面是一致的，一方输入，另一方可以实时显示，就像在线聊天室一样。</li><li>如同现代浏览器一样，还可以通过不同的窗口来实现多标签页。</li><li>可以通过把一个窗口切割成多个窗格的方式，来有效分配窗口显示资源，提高屏幕占用效率。</li></ul><p>下面看看 tmux 复用后的实际效果：</p><p><img src="http://data.kchen.cc/mac_qrsync/e63751170c3cc32863ada94b1527f581.png-960.jpg" alt="mutiplexer"></p><p>左下角显示了当前我所在的会话名称 <code>Test</code> 以及当前窗口和窗格编号 <code>1-3</code>，下方中间部分显示了该会话下的窗口，当前激活的是窗口 <code>[1:htop]</code>，右下角是系统时间。这是我自己配置的状态栏。</p><p>上方主体部分是当前窗口的窗格分割：</p><p><img src="http://data.kchen.cc/mac_qrsync/2fe242a247e55ffecc33467aede4259a.png-960.jpg" alt="panes"></p><p>我在 1 中打开了 vim 编写文本，在 2 中使用 <code>htop</code> 查看系统进程，在 3 中准备执行某个命令。三个窗格互不干扰，平行工作，而且优雅整洁。</p><p>窗口 <code>[2:~/blog]</code> 中我还跑着我的博客服务器，和相关的配置等等，两边泾渭分明。</p><h2 id="tmux-的安装和使用"><a href="#tmux-的安装和使用" class="headerlink" title="tmux 的安装和使用"></a>tmux 的安装和使用</h2><h3 id="安装-tmux"><a href="#安装-tmux" class="headerlink" title="安装 tmux"></a>安装 tmux</h3><p>先通过查看 tmux 的版本号，自己是不是已经装了 tmux：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tmux -V</span><br></pre></td></tr></table></figure><p>后续配置是针对 <code>2.5</code> 版本进行的，请确认自己的 tmux 的版本是 <code>2.5+</code>。</p><h4 id="MacOS"><a href="#MacOS" class="headerlink" title="MacOS"></a>MacOS</h4><p>建议使用 <code>homebrew</code> 来安装：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew install tmux</span><br></pre></td></tr></table></figure><h4 id="Ubuntu"><a href="#Ubuntu" class="headerlink" title="Ubuntu"></a>Ubuntu</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo apt-get install tmux</span><br></pre></td></tr></table></figure><blockquote><p>如果 apt 源里面的 tmux 版本过于老旧，可以选择使用下面的办法从源码编译安装最新版本的 tmux。</p></blockquote><h4 id="CentOS"><a href="#CentOS" class="headerlink" title="CentOS"></a>CentOS</h4><p>这个要自己编译安装最新版本，我们先安装依赖：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yum install gcc kernel-devel make ncurses-devel</span><br></pre></td></tr></table></figure><p>下载 <code>libevent</code> 并编译安装</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">curl -OL https://github.com/libevent/libevent/releases/download/release-2.0.22-stable/libevent-2.0.22-stable.tar.gz</span><br><span class="line">tar -xvzf libevent-2.0.22-stable.tar.gz</span><br><span class="line"><span class="built_in">cd</span> libevent-2.0.22-stable</span><br><span class="line">./configure --prefix=/usr/<span class="built_in">local</span></span><br><span class="line">make</span><br><span class="line">sudo make install</span><br></pre></td></tr></table></figure><p>下载 tmux v2.8 并编译安装</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">curl -OL https://github.com/tmux/tmux/releases/download/2.8/tmux-2.8.tar.gz</span><br><span class="line">tar -xvzf tmux-2.8.tar.gz</span><br><span class="line"><span class="built_in">cd</span> tmux-2.8</span><br><span class="line">./configure &amp;&amp; make</span><br><span class="line">sudo make install</span><br></pre></td></tr></table></figure><h3 id="安装-k-tmux-配置"><a href="#安装-k-tmux-配置" class="headerlink" title="安装 k-tmux 配置"></a>安装 k-tmux 配置</h3><h4 id="推荐方式"><a href="#推荐方式" class="headerlink" title="推荐方式"></a>推荐方式</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">1. 如果需要的话，备份原有 tmux 配置</span><br><span class="line"></span><br><span class="line">cp ~/.tmux.conf ~/.tmux.conf_bak</span><br><span class="line"></span><br><span class="line">2. 获取配置文件</span><br><span class="line"></span><br><span class="line">curl https://raw.githubusercontent.com/kchen0x/k-tmux/master/tmux.conf &gt; ~/.tmux.conf</span><br><span class="line"></span><br><span class="line">3. 完成安装</span><br></pre></td></tr></table></figure><h4 id="使用-Github-软链接方式"><a href="#使用-Github-软链接方式" class="headerlink" title="使用 Github 软链接方式"></a>使用 Github 软链接方式</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> https://github.com/kchen0x/k-tmux.git</span><br><span class="line">ln -s <span class="variable">$PWD</span>/k-tmux/tmux.conf ~/.tmux.conf</span><br></pre></td></tr></table></figure><blockquote><p>若使用软链接的方式，不要删除 k-tmux 的真实目录。</p></blockquote><h4 id="插件"><a href="#插件" class="headerlink" title="插件"></a>插件</h4><p>k-tmux 的配置中使用了 TPM(Tmux Plugin Manager) 来管理插件，并且开启了其中的 prefix highlight 功能，可以指示当前的 prefix 状态和复制模式。如要启动需要先安装插件：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git <span class="built_in">clone</span> https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm</span><br></pre></td></tr></table></figure><p>安装完成后进入 tmux，使用[prefix]加大写<code>I</code>重载一次插件即可（[prefix]为<code>ctrl+a</code>）。</p><h3 id="使用-tmux"><a href="#使用-tmux" class="headerlink" title="使用 tmux"></a>使用 tmux</h3><blockquote><p>以下教程基于我的个人配置讲解，小部分特性和快捷键与官方默认有出入（不同的地方会做出说明），特此声明。另外，为了能够获得最佳体验，请使用 Solarized dark 配色，详情请见 <a href="http://kchen.cc/2016/11/16/solarized-colorscheme/">「八卦阴阳鱼，谈谈 Solarized 配色」</a>。</p></blockquote><h3 id="简要说明"><a href="#简要说明" class="headerlink" title="简要说明"></a>简要说明</h3><blockquote><p>操作前缀 <code>[PREFIX-]</code> ：tmux 中所有的命令都需要先按下操作前缀 <code>ctrl+a</code>。（官方默认为 <code>ctrl+b</code>，已更改，主要是为了和 screen 保持一致，同时也更方便按）</p></blockquote><h4 id="会话操作"><a href="#会话操作" class="headerlink" title="会话操作"></a>会话操作</h4><blockquote><p>下面有 <code>$</code> 命令行提示符的是在原生终端中输入的直接作用于 tmux server 的命令。此外都在 tmux 的会话中键入。注意区分大小写。</p></blockquote><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建, tmux new -s &lt;name-of-my-session&gt; 创建一个新的会话</span></span><br><span class="line">$ tmux new -s basic</span><br><span class="line"></span><br><span class="line"><span class="comment"># 在tmux中创建一个会话</span></span><br><span class="line">[PREFIX-:] new -s &lt;name-of-my-session&gt;</span><br><span class="line"></span><br><span class="line"><span class="comment"># 分离会话 detach</span></span><br><span class="line">[PREFIX<span class="_">-d</span>]</span><br><span class="line">[detached (from session basic)]</span><br><span class="line">or</span><br><span class="line">$ tmux detach</span><br><span class="line">or</span><br><span class="line">[PREFIX-Ctrl-z]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看已有会话列表(list-session)</span></span><br><span class="line">$ tmux ls</span><br><span class="line">basic: 1 windows (created Wed Aug  5 14:54:04 2015) [200x49]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 在tmux中查看会话列表并切换</span></span><br><span class="line">[PREFIX<span class="_">-s</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 连接会话(只有一个)</span></span><br><span class="line">$ tmux attach</span><br><span class="line">$ tmux attach -t basic</span><br><span class="line">$ tmux a -t basic</span><br><span class="line"></span><br><span class="line"><span class="comment"># 杀掉会话</span></span><br><span class="line">$ tmux <span class="built_in">kill</span>-session -t</span><br><span class="line"></span><br><span class="line"><span class="comment"># 重命名会话</span></span><br><span class="line">[PREFIX-$] 之后输入名字回车</span><br></pre></td></tr></table></figure><h4 id="窗口操作"><a href="#窗口操作" class="headerlink" title="窗口操作"></a>窗口操作</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 创建一个新的窗口</span></span><br><span class="line">[PREFIX-c]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 重命名一个窗口（配置开启动态窗口重命名，会根据当前命令自动更改）</span></span><br><span class="line">[PREFIX-,] 之后输入名字回车</span><br><span class="line"></span><br><span class="line"><span class="comment"># 切换到下一个窗口, k-tmux另外配置了PREFIX-t/T</span></span><br><span class="line">[PREFIX-n]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 切换到对应窗口</span></span><br><span class="line">[PREFIX-1/2/3]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 可视化选择切换到的窗口</span></span><br><span class="line">[PREFIX-w]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查找窗口</span></span><br><span class="line">[PREFIX<span class="_">-f</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 退出窗口</span></span><br><span class="line"><span class="built_in">exit</span> or</span><br><span class="line">[PREFIX-&amp;] 会有确认</span><br></pre></td></tr></table></figure><h4 id="窗格操作"><a href="#窗格操作" class="headerlink" title="窗格操作"></a>窗格操作</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 垂直/水平分割窗口（原先未修改键位的分割方式是[PREFIX-%]和[PREFIX-"]）</span></span><br><span class="line">[PREFIX-\] / [PREFIX--]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 关闭一个窗格, 要确认</span></span><br><span class="line">[PREFIX-x]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 或者</span></span><br><span class="line"><span class="built_in">exit</span> [窗格里执行]</span><br><span class="line"></span><br><span class="line">===begin 窗格切换</span><br><span class="line"><span class="comment"># 窗格之间移动，改命令可以在 1500 ms 内连续使用</span></span><br><span class="line">[PREFIX-hjkl]</span><br><span class="line">or</span><br><span class="line">[PREFIX-↑↓←→]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 最近使用两个窗口之间切换</span></span><br><span class="line">[Ctrl-\]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 展示窗口数字并选择跳转</span></span><br><span class="line">[PREFIX-q]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 循环切换</span></span><br><span class="line">[PREFIX-o]</span><br><span class="line">===end</span><br><span class="line"></span><br><span class="line">===begin 窗格大小调整</span><br><span class="line"><span class="comment"># 窗格大小调整</span></span><br><span class="line">[PREFIX-HJKL]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 暂时把当前窗格最大化，再按一次变回来</span></span><br><span class="line">[PREFIX-z]</span><br><span class="line">===end</span><br><span class="line"></span><br><span class="line"><span class="comment"># 关闭当前窗格, 需确认</span></span><br><span class="line">[PREFIX-x]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 移动当前窗格到左边/右边</span></span><br><span class="line">[PREFIX-&#125;] / [PREFIX-&#123;]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 在新窗口中打开当前窗格</span></span><br><span class="line">[PREFIX-!]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 自动轮流切换官方默认的多种布局</span></span><br><span class="line">[PREFIX-space]</span><br></pre></td></tr></table></figure><h4 id="复制粘贴"><a href="#复制粘贴" class="headerlink" title="复制粘贴"></a>复制粘贴</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 进入复制模式</span></span><br><span class="line">[PREFIX-[]</span><br><span class="line"></span><br><span class="line"><span class="comment"># =&gt; 可以进行的操作（和 VIM 保持一致）</span></span><br><span class="line">space/v    开始选择</span><br><span class="line">Ctrl-v     整块选择</span><br><span class="line">hjkl       方向键移动</span><br><span class="line">w/b        向前向后移动一个单词</span><br><span class="line">fx/Fx      行内移动到下一个字符位置</span><br><span class="line">ctrl-b/f   在缓冲区里面翻页</span><br><span class="line">g/G        到缓冲区最顶/底端</span><br><span class="line">/ ?        向下, 向上查找</span><br><span class="line">n/N        查找后下一个, 上一个</span><br><span class="line">Enter/y    复制</span><br><span class="line">[PREFIX-]] 粘贴</span><br><span class="line"></span><br><span class="line"><span class="comment"># 缓冲区相关</span></span><br><span class="line"><span class="comment"># 复制整个窗格可见区域</span></span><br><span class="line">[PREFIX-:] capture-pane</span><br><span class="line"></span><br><span class="line"><span class="comment"># 查看缓冲区内容</span></span><br><span class="line">[PREFIX-:] show-buffer</span><br><span class="line"></span><br><span class="line"><span class="comment"># 列出缓冲区列表</span></span><br><span class="line">[PREFIX-:] list-buffers</span><br><span class="line"></span><br><span class="line"><span class="comment"># 从缓冲区列表选择并插入到当前窗格</span></span><br><span class="line">[PREFIX-:] choose-buffer =&gt; 回车</span><br></pre></td></tr></table></figure><h4 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h4><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 获得快捷键列表</span></span><br><span class="line">[PREFIX-?]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 快速在分割窗格中查看 man 手册 &lt;command&gt;命令</span></span><br><span class="line">[PREFIX-/ &lt;<span class="built_in">command</span>&gt;]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 进入命令模式</span></span><br><span class="line">[PREFIX-:]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 一些命令模式下的命令</span></span><br><span class="line"><span class="comment"># 新建窗口</span></span><br><span class="line">new-window -n console</span><br><span class="line"></span><br><span class="line"><span class="comment"># 新建并执行命令</span></span><br><span class="line">new-window -n processes <span class="string">"top"</span></span><br></pre></td></tr></table></figure><h3 id="鼠标模式"><a href="#鼠标模式" class="headerlink" title="鼠标模式"></a>鼠标模式</h3><p>tmux 最大的优点是全键盘操作，使得很多操作变得快捷又优雅。但是缺点也是显而易见的，就是有些时候不大方便：</p><ul><li>回滚历史记录必须 <code>PREFIX-[</code> 进入复制模式，然后通过 VIM 风格进行翻页。（jk和 <code>ctrl-u</code>/<code>ctrl-d</code>）</li><li>调整窗格大小不太方便。</li></ul><p>所以为了照顾鼠标用户，我在配置中默认开启了鼠标模式（mouse reporting）。</p><p>在鼠标模式下，你可以：</p><ul><li>直接使用滚轮进入复制模式回滚查看历史记录，滚回底部或 <code>q</code> 键退出复制模式。</li><li>选择文本即可完成复制。</li><li>直接拖动窗格边界调整窗格大小。</li><li>点击状态栏的标签，切换窗口。</li></ul><p>建议大家还是多使用快捷键完成操作，增加工作效率。</p><p>想要关闭关闭鼠标模式，请进入命令模式然后：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 进入命令模式</span></span><br><span class="line">[PREFIX-:]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 关闭鼠标模式</span></span><br><span class="line">setw -g mouse off</span><br></pre></td></tr></table></figure><p>当然也可以直接去配置文件里修改相关的配置。想要修改的配置立马生效，可以使用：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 重载 tmux 配置</span></span><br><span class="line">[PREFIX-R]</span><br></pre></td></tr></table></figure><p>最后再提醒一次大家，善用 <code>[PREFIX-?]</code> 快捷键帮助，再配合 <code>/</code> 查找命令，使用起来便可如鱼得水。祝大家使用愉快！</p>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;本文是项目 k-tmux 的说明文档，介绍了一套 tmux 的配置。本配置受到 @wlken 的启发。&lt;/p&gt;
&lt;p&gt;ChangeLog: 2018-10-19 更新了弃用的配置命令，完善对鼠标的支持，修改了关闭窗口的键位绑定，安装并使用插件，增加对 prefix 状态和 buffer 复制状态的显示。&lt;/p&gt;
    
    </summary>
    
    
      <category term="tmux" scheme="http://kchen.cc/tags/tmux/"/>
    
      <category term="terminal" scheme="http://kchen.cc/tags/terminal/"/>
    
      <category term="multiplexer" scheme="http://kchen.cc/tags/multiplexer/"/>
    
  </entry>
  
  <entry>
    <title>iTerm2 与 Tmux 的集成</title>
    <link href="http://kchen.cc/2016/11/17/iterm2-and-tmux-integration/"/>
    <id>http://kchen.cc/2016/11/17/iterm2-and-tmux-integration/</id>
    <published>2016-11-17T00:43:58.000Z</published>
    <updated>2021-08-31T01:59:39.004Z</updated>
    
    <content type="html"><![CDATA[<link rel="stylesheet" class="aplayer-secondary-style-marker" href="/assets/css/APlayer.min.css"><script src="/assets/js/APlayer.min.js" class="aplayer-secondary-script-marker"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/hint.css/2.4.1/hint.min.css"><p><img src="https://camo.githubusercontent.com/9a07003e528862ebff782aac2220ef226d28db57/68747470733a2f2f7261772e6769746875622e636f6d2f636872697368756e742f636f6c6f722d736368656d65732f6d61737465722f7468617965722f73637265656e73686f742e706e67" alt="tmux"></p><p>iTerm2 最新的特性已经集成了 tmux，这意味着什么？</p><p>通常情况下，当你使用 tmux 的时候，会在一个「物理」窗口（Window）中创建多个虚拟的窗口。你可以通过在 tmux 中使用各种命令来操作它的环境，但这样也会随之带来一些问题：</p><ul><li>你需要敲下前缀修饰键来进入 tmux 的命令模式（默认情况下是<code>control + b</code>，这和 emacs 中的左移光标是冲突的，而且这也会让与 shell 的交互变得更加困难）。</li><li>你需要不止一次的使用 ssh 来连接到远程服务器（remote host）以获得不止一个的 tmux 会话（session）窗口。</li><li>你需要学习 tmux 的命令。</li><li>你需要开启鼠标报告（mouse reporting）来调整分割窗格（pane）的大小，尽管你并不想启用它。</li><li>当你使用 tmux 的时候，一些终端模拟器内置的功能不能很好工作，比如说：你并不能像在普通的终端窗口中那般快捷的使用回滚查看历史，同时，tmux 的查找功能也完全跟 iTerm2 的不能比拟。</li></ul><p>对于大多数的用户而言，在终端中使用复用器（Multiplexer）是十分好用的工作方式，但是他们并不想接受以上的种种缺陷。</p><a id="more"></a><p>iTerm2 与 tmux 的集成（iTerm2’s tmux integration）就解决了这些痛点。</p><p>当你执行 <code>tmux -CC</code> 命令时一个新的 tmux 会话就会被创建，一个看上去和普通 iTerm2 窗口没有差别的窗口将会被打开。唯一不同的地方就是，当 iTerms2 退出或者是 ssh 会话丢失时，tmux 会保持运行。你可以重新连接上刚刚 ssh 连接的远程主机，然后执行 <code>tmux -CC attach</code> 命令，iTerm2 窗口会重新打开并恢复到断开时相同的状态。那么，一些应用场景就不难想象了：</p><p>对于那些常常使用 ssh 的小伙伴来说，你可以：</p><ul><li>回到家中然后恢复公司的工作环境。</li><li>不必担心系统升级的电脑重启。</li></ul><p>而对于所有小伙伴而言，你可以：</p><ul><li>通过连接同一个 tmux 会话和别的小伙伴协作（collaborate）。</li><li>保护自己不因 iTerm2 崩溃（iTerm3 会通过会话修复特性来减轻这种状况）而丢失工作环境。</li></ul><h2 id="用法"><a href="#用法" class="headerlink" title="用法"></a>用法</h2><p>你可以一如往常那般使用 tmux，只需要在末尾加上 <code>-CC</code> 参数就可以了，实际上，也就是执行以下任意一个命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">tmux -CC</span><br><span class="line">tmux -CC attach</span><br></pre></td></tr></table></figure><p>当你执行 <code>tmux -CC</code> 命令的时候，你将会在终端中看到如下的菜单：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">** tmux mode started **</span><br><span class="line"></span><br><span class="line">Command Menu</span><br><span class="line">----------------------------</span><br><span class="line">esc    Detach cleanly.</span><br><span class="line">  X    Force-quit tmux mode.</span><br><span class="line">  L    Toggle logging.</span><br><span class="line">  C    Run tmux <span class="built_in">command</span>.</span><br></pre></td></tr></table></figure><ul><li>如果你按下 <code>esc</code> 键，tmux 窗口会关闭，tmux 客户端也会终止。</li><li>如果你按下 <code>esc</code> 键但是任何事情都没有发生，这说明 tmux 的客户端可能崩溃了或者是除了别的状况。这时按下 <code>X</code> 键来强制 iTerm2 退出 tmux 模式。如果真是的 tmux 客户端崩溃的话，你也许会需要执行 <code>stty sane</code> 命令来恢复你的终端状态。</li><li>如果你想提交一个 Bug 的话，可以通过按下 <code>L</code> 键来重现问题，tmux 协议命令会被打印到屏幕上。</li><li>如果你想执行菜单中没有的命令，你可以按下 <code>C</code> 键来进入 tmux 命令模式，一个可以输入命令的对话框将会弹出，你可以键入类似 <code>new-window</code> 这样的命令。</li></ul><p>通常情况下， 大多数的动作都不需要通过键入命令来实现，以下的一些 iTerm2 的动作就可以直接作用于 tmux：</p><ul><li>关闭会话，标签页（tab）或者是窗口：终止 tmux 会话或窗口。</li><li>分割窗格：通过 <code>split-window</code> 分割 tmux 窗口。</li><li>调整窗格大小：通过 <code>resize-pane</code> 命令调整 tmux 窗格大小。</li><li>调整窗口大小：告诉 tmux 客户端的大小改变了，重调所有窗口的大小。窗口不会大于它连接（attach）的最小的客户端的大小，一个灰色的区域将会出现的窗口的右下方表明实际窗口的大小超出了 tmux 窗口允许的最大大小。这一原则的一个好处就是所有的 tmux 窗口/标签页都包含完全相同的行数和列数。</li><li>通过菜单栏 Shell-&gt;tmux 创建一个新的窗口或者标签页：创建一个新的 tmux 窗口。</li><li>通过菜单栏 Shell-&gt;tmux-&gt;Detach 断开（detach）与 tmux 会话的连接：断开与 tmux 会话的连接，所有 tmux 窗口都会被关闭，你可以通过 <code>tmux -CC attach</code> 命令重新与之连上。</li></ul><h2 id="限制"><a href="#限制" class="headerlink" title="限制"></a>限制</h2><p>大多数的限制都将会在接下来的版本中得到解决和改进：</p><ul><li>在早于2.9版本的 iTerm2 中，你只能同时连接上一个 tmux 会话。在2.9和更新的版本中，你可以同时连接多个 tmux 会话。</li><li><code>.tmuxrc</code> 文件未经测试，可能运转不正常。</li><li>在早于2.9版本的 iTerm2 中，不能最大化窗格，已经在2.9版本中解决了这一问题。</li></ul><h2 id="创建-tmux"><a href="#创建-tmux" class="headerlink" title="创建 tmux"></a>创建 tmux</h2><p>你需要使用1.8或更高版本的 tmux，在 Mac 上安装 tmux 最简便的方法是使用 <code>homebrew</code>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">brew install tmux</span><br></pre></td></tr></table></figure><blockquote><p>原文链接 <a href="https://gitlab.com/gnachman/iterm2/wikis/TmuxIntegration" target="_blank" rel="noopener">《iTerm2 and tmux Ingeration》</a></p></blockquote>]]></content>
    
    <summary type="html">
    
      &lt;p&gt;&lt;img src=&quot;https://camo.githubusercontent.com/9a07003e528862ebff782aac2220ef226d28db57/68747470733a2f2f7261772e6769746875622e636f6d2f636872697368756e742f636f6c6f722d736368656d65732f6d61737465722f7468617965722f73637265656e73686f742e706e67&quot; alt=&quot;tmux&quot;&gt;&lt;/p&gt;
&lt;p&gt;iTerm2 最新的特性已经集成了 tmux，这意味着什么？&lt;/p&gt;
&lt;p&gt;通常情况下，当你使用 tmux 的时候，会在一个「物理」窗口（Window）中创建多个虚拟的窗口。你可以通过在 tmux 中使用各种命令来操作它的环境，但这样也会随之带来一些问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;你需要敲下前缀修饰键来进入 tmux 的命令模式（默认情况下是&lt;code&gt;control + b&lt;/code&gt;，这和 emacs 中的左移光标是冲突的，而且这也会让与 shell 的交互变得更加困难）。&lt;/li&gt;
&lt;li&gt;你需要不止一次的使用 ssh 来连接到远程服务器（remote host）以获得不止一个的 tmux 会话（session）窗口。&lt;/li&gt;
&lt;li&gt;你需要学习 tmux 的命令。&lt;/li&gt;
&lt;li&gt;你需要开启鼠标报告（mouse reporting）来调整分割窗格（pane）的大小，尽管你并不想启用它。&lt;/li&gt;
&lt;li&gt;当你使用 tmux 的时候，一些终端模拟器内置的功能不能很好工作，比如说：你并不能像在普通的终端窗口中那般快捷的使用回滚查看历史，同时，tmux 的查找功能也完全跟 iTerm2 的不能比拟。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对于大多数的用户而言，在终端中使用复用器（Multiplexer）是十分好用的工作方式，但是他们并不想接受以上的种种缺陷。&lt;/p&gt;
    
    </summary>
    
    
      <category term="tmux" scheme="http://kchen.cc/tags/tmux/"/>
    
      <category term="iTerm2" scheme="http://kchen.cc/tags/iTerm2/"/>
    
  </entry>
  
</feed>
