linux下的session leader及controlling terminal

最近在调试嵌入式下设备启动基于busybox的init程序所执行的rc.sysinit脚本时,发现该脚本执行的后台脚本,会因为没有关联到串口设备(通过ps的TTY一列可以确认) ,导致我们做了tty设备检测的程序无法顺利执行,借此机会,查阅了busybox的init代码,并对linux的session group和controlling terminal有了一定的理解。 在libc的info文档中Job Control一节,有如下几个重要的定义: process group,即进程组,用管道符创建的一堆命令就行成了一个进程组。 session,即会话,通常从login shell登陆后的所有进程都属于一个session,即session由login shell创建,注意libc的文档还是从传统的语义来讲的,对于现代的大多数桌面环境而言(DE,如KDE/GNOME等),一个图形会话,往往会通过setsid函数起大量的session),一个session下可以管理多个process group,一个进程可以在同一个session下的不同process group中迁移,但将一个进程移到另外的会话中则只能通过setsid这个函数来创建新的会话实现。 session leader,即会话leader,创建会话的进程称为会话leader,从上面的描述可知,login shell一般为会话leader,首次调用setsid的进程也将成为session leader。 controlling terminal,即控制终端,进程的重要属性之一,由fork创建的子进程继续父进程的controlling terminal,而从setsid的libc文档中可以看出(或者用man 3 setsid查看),setsid调用后,进程将做为新的会话的会话leader,并且丢失controlling terminal属性,而其后该会话leader打开的首个tty设备,将成为该会话的controlling terminal(见附2说明);shell通常只会将controlling terminal这个属性给予一个进程组,以便由这个进程组通过终端设备获取输入或者输出信息,这组进程将称为前台任务,而未获得输入或输出等权限的进程组,在尝试向controlling terminal读取或写入数据时,将收到SIGTTIN或SIGTTOU信号,默认情况下这个信号将中止相关程序的执行。这点是合理的,因为如果不做此限制,后台程序很可能扰乱终端输出或者输入的处理。 经过上述讲述,以代码形式给出上述概念的演示。 #include "stdlib.h" #include "errno.h" #include "unistd.h" #include "fcntl.h" int main() { int err, fd; pid_t pid; char *pts_name; pid = fork(); if (pid != 0) { exit(0); // 主进程退出。 } pid = setsid(); // 在子进程中创建新的会话 // 打印从父进程继续而来的tty,注意,该tty已经不是我们的controlling terminal printf("before we prepare the new stdio, the child inherit its parent's stdio %s\n", ttyname(0)); // 如下一段代码用于创建一个pseudo-terminal,将作为tty设备被新的会话leader打开,成为该会话的controlling terminal fd = getpt(); pts_name = ptsname(fd); printf("allocated a new pts is %s\n", pts_name); grantpt(fd); unlockpt(fd); // 首次open的tty设备将成为controlling terminal fd = open(ptsname(fd), O_RDWR); // 将该fd做为标准输入,在本例中意义不大 close(STDIN_FILENO); dup2(fd, STDIN_FILENO); // 停留2分,以便我们可以通过ps检验是否获得了新的session及controlling terminal sleep(120); } 编译并执行上述程序后,执行后,立即用如下命令,可以查看到新进程确实拥有了新的controlling terminal了。 ...

September 25, 2015 · 1 min · fortitude.zhang

shell中通过set builtin操作positional parameter

最近在调试ubuntu 14.04上的openvswitch服务时,发现openvswitch在upstart里的脚本实现频繁用到了set这样一个shell builtin,代码写的不是很好理解,后来经过查询shell相关文档,终于理解了通过set这个builtin来操作位置变量确实是一个很不错的办法。 bash的info文档中专门有一章节来介绍The Set Builtin,并且由于builtin is so complicated that it deserves its own section,但从本文的角度来看,我们认为set就是用来操作位置变量的,以下用一个简单的示例,就可以很容易的给出set的妙用了。 set -- # --这个特殊的用法来清空位置变量,即用$@来访问位置变量将得到空,除了--,set还有其他很多用法,具体可以查看shell的info文档 set "$@" ls # 用ls来初始化位置变量,ls将被赋值给$1 set "$@" -l # 将$@展开后,与-l一起重新赋值给位置变量,使得ls被赋值给$1,-l给赋值给$@ $@ # 展开位置变量,并执行,等同于执行ls -l命令 从上面的代码示例可以看出,set可以将其后的一串参数赋值给位置变量,从而方便对命令行扩充,支持更好的扩展性,并且由于@这个位置变量全集的通用性,使得在脚本嵌套时,可以方便在被执行的脚本中,对原调用脚本传过来的参数进行进一步的编辑,从而实现更为复杂的应用,比如上述openvswitch的upstart脚本,就在这点上大做了文章。 从这点感觉,bash设计的还是很精妙的,工作中大多数采用C编程,对于shell脚本的编程实践较少,后续需要继续掌握这些细节和强大之处:)

August 27, 2015 · 1 min · fortitude.zhang

Linux普通时钟与高精度时钟

对于X86 PC来说,主板上会有一个用电池驱动的里存在的以较小hz(如200)这样的interrupt(即tick interrupt),形成以1/hz为单位的jiffes,精度约在ms上的时钟,无法满足高精度计时(ns级)的需要(计时、ntp同步)等 。 于是,通过其他芯片来触发高精度中断,成为linux os提供高精度时间源的一个可能的选择。 对于linux系统,可能过如下命令查看可用的时间源以及当前活跃的时间源: cat /sys/devices/system/clocksource/clocksource0/available_clocksource cat /sys/devices/system/clocksource/clocksource0/current_clocksource 目前在x86架构上使用较多的为tsc这个由x86 cpu内部以cycle计算提供的一个计数器( TSC)来实现的由中断触发的时间源,可以以clock_event_device的行式来触发linux中断执行,从而驱动linux的高精度计时功能。

June 27, 2015 · 1 min · fortitude.zhang

谈谈Linux下的时间机制

最近同事在解决一个时区问题时花费了较多时间,但仍是一头雾水,通过查询Linux相关的文档,我给出了他一些修改建议,经过验证后问题得以修复,事后我总结来看,主要是该同事对于Linux下的时间机制的理解不够深入,因此这里我将相关的知识给记录下来,以解释相关的疑惑。 Linux下目前有三种主要的时钟,其名称及作用如下: 硬件时钟 此时钟一般由主板上的单独电子器件提供,它与CPU核心是独立的运行的,往往在电脑关机时仍能继续运行(想想我们的PC在装机重启后,时间仍然还保持着正确性就是因为这么个东西存在)。 此时钟还有一些其他名称,如RTC(实时时钟)、BIOS时钟或CMOS时钟,此时钟可以通过hwclock这样的小工具来设置。 需要注意的是,硬件时钟中是不感知时区的,其硬件实现记录了个初始值(如我司设备经常采用的1970.1.1等),然后用户可以通过hwclock写入一个时间来更新它,我们可以认为它是一个有初始值的计数器,用户可以更新当前的值,之后它将按照其计时精度来更新其值,供系统通过硬件接口读取。 系统时钟 此时钟是由Linux内核所维护的一个软件时钟,由时钟中断触发更新,其时间是相对于1970.1.1-00:00:00 UTC这个时间的秒数(UTC与GMT即格林威治时间意义相同,为世界标准时间),为方便这里的讲述,我们可以假定我们在格林威治这个地方讨论时间,这样我们可以先把时区放到一边不管了。 在Linux启动后,系统启动脚本(不管是sysvinit还是systemd都将有这样的处理)将通过hwclock读取硬件时钟,并将时间设置到内核中,此后用户获取时间均通过内核获取。 单调时钟 在Linux系统上,用户可以通过date命令更新系统时间,从而导致系统时钟出现跳变,这对于一些涉及到对调度单元(如协程)运行时间来记时以便切出去让其他进程执行的程序来说则会产生计时不准的问题,为此,Linux上支持了一个所谓的单调时钟的接口,此接口除支持单调时钟外,还支持对于系统时钟的获取,此接口属于较新的POSIX标准,如果Linux版本较老的话,可能将无法支持 此接口的定义如下: int clock_gettime(clockid_t clk_id, struct timespec *tp); // 采用CLOCK_MONOTONIC这个clk id可以获取到单调时钟 上面讨论时,我刻意互略了关于时区的处理,为简单起见,我们这里可以认为Linux内核是不感知时区的(实际上由于种种原因比如VFAT文件系统的时间戳处理就需要内核需要感知到时区)。 在Linux下,由于大量程序均需要依赖于glibc,因此glibc会读取TZ这个环境变量来设置时区,下面是一些C库函数的简单介绍: struct tm *gmtime(const time_t *timep); // 获取UTC时间,也就是说怎么改TZ变量或者通过tzset更改时区都不会影响此值 struct tm *localtime(const time_t *timep); // 获取本地时间,更改TZ变量或者通过tzset更改时区都会影响此值 当然当于时区还需要补充一点的是,环境变量这个东西只影响所属的进程,在子进程中修改并不会影响父进程,libc.so甚至提供了一个全局变量用来保存当前进程的时区设置(注意,虽然libc.so为动态库,但其在mmap映射是private的,所以会导致其中定义的全局变量每个进程都复制了一份),如果修改的话,需要通过signal类似的IPC机制通知相关进程予以刷新,这也是我同事改时区反复出问题的一个原因。 从一个编程者的角度来看,如果是涉及到对调度单元(如协程)运行时间记时的话,要尽可能采用单调时钟,下面是syslog-ng项目所采用的异步io库ivykis关于时间处理的代码片段,可以看出,它将尽可能去使用单调时钟或者clock_gettime接口,在最后才尝试使用gettimeofday来获取时间。 #ifdef HAVE_CLOCK_GETTIME static int clock_source; #endif void iv_time_get(struct timespec *time) { struct timeval tv; #if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_MONOTONIC_FAST) if (clock_source < 1) { if (clock_gettime(CLOCK_MONOTONIC_FAST, time) >= 0) return; clock_source = 1; } #endif #if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_MONOTONIC) if (clock_source < 2) { if (clock_gettime(CLOCK_MONOTONIC, time) >= 0) return; clock_source = 2; } #endif #if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_REALTIME) if (clock_source < 3) { if (clock_gettime(CLOCK_REALTIME, time) >= 0) return; clock_source = 3; } #endif gettimeofday(&tv, NULL); time->tv_sec = tv.tv_sec; time->tv_nsec = 1000L * tv.tv_usec; }

January 31, 2015 · 1 min · fortitude.zhang

精神自由

昨晚晚饭时喝了一杯咖啡,于是在夜半时一如往常一样的失眠了,而关于自己此生到底要追求什么这样一个问题,其实在我心里已经盘旋了很久,于是借着昨晚的时间,我好好思考了这个问题,而结果既出于意料之外,又在意料之中,仔细推敲来,我其实一直所追求的是一种自由,但这种自由并非是消费的自由,也并非旅行的自由,虽然这种自由也建构在金钱的基础之上,但这种自由则是思考的自由,工作方式的自由,睡眠的自由,读书的自由,写作的自由等等,而如果要从这些这些自由中抽象出共性出来,那就是精神的自由,或者说是精神层面的自由。 今年是二零一五年,也是我进入三十岁奔向四十岁的关键一年,对我来说,工作、结婚、生子、逐渐年迈的父母、偿还房贷等等事务,都会成为我要面对的现实问题,而我能拿出来安慰自己的,则是我相信自己还能够抽出额外的时间,进行思考、阅读甚至这里的写作这样片刻属于我的时间,而我相信我所追求的,就是我能够在这些事务与自己的内心世界之间寻找到一种平衡,我能够坚守这这块属于我的土地,但却又能维持好前述种种。 从目前来看,实现这种自由的前提很世俗,就是资金,但相比财务自由来说,我的要求却低的很,或许是因为成长于农村的原因,我生活并不奢华,也不想追求奢华,于我而言,我只需要简单而基本的生活,于是我有理由相信自己实现这个过程较为简单,它应该是两部分,首先我可以拥有一个覆盖日常生活开支的固定收入来源,比方说一套用于出租的房产或者其他较为稳健的理财产品,之后是我将从事相对灵活自由的工作,像我这样的职业,在互联网这样的时代,想做自由职业者应该容易的多;通过这两部分的结合,我希望再经过十年,也即我四十岁的时候,我能够实现自己这里所谈到的精神自由的资金基础。 想清楚自己追求之后,对于我而言,最大的作用是能够理性正视生活中所遇到的事情,哪些事情我需要隐忍,哪些事情我需要放手一博,哪些事情我可以做,哪些事情我不可以做,也有助于我更好的做好人生规划,不偏离人生方向的持续前行。 我很开心在自己即将在而立之年能想通这样一件事,于是就记录在网络上的这片属于自己的地方,期待将来我回过头来再来看看。 附: 当我发布这篇文章到网站上后,才发现原来我网站的标题就是通往成熟与自由的旅程,看来这并不是巧合,而是我潜意识中故有的所在了。

January 30, 2015 · 1 min · fortitude.zhang

Midonet聪明的Tunnel Key分配策略

拥抱开源如今成了一种潮流,既Juniper家的OpenContrail成为开源一揽子(之所以称为一揽子是用于区别现有OpenStack中由社区所维护的基于分散组件的松耦合方式)OpenStack网络虚拟化方案的首发明星后,Midukura这家公司也将自家的OpenStack网络虚拟化方案以开源方式来运作了,相比于OpenContrail的工程师的设备商研发背景,Midukura更具有IT运维背景(这点可以从其技术堆栈如scala编程语言、zookeeper/cassandra数据库、分布式架构推测出来,关于更细节的内容,后面我会陆续补充相关的分析),因此我个人还是更看好Midokura的方案,而从自己实际部署及验证的情况来看,Midokura要靠谱的多一点,当然这只是一家之言,也并非本文的重点,就不再展开。 在本文中我想谈的是,Midonet Tunnel Key的使用策略,与我们通常在基于OVS的方案中看到Tunnel Key(不论是NVGRE还是VXLAN)一般用做租户标识(即Virtual Network Identifier)不同,Midonet Tunnel Key则可以理解为按VIF分配的(实际上是基于Midonet的外部虚拟端口分配的,这里为了简化理解,可以简单认为就是按VIF分配的),而前者其实也是IETF相关标准文档所描述的用法。 Midonet之所以这样做的原因,与其架构实现有较大的关系,Midonet的架构总结起来,有如下的这几种特色: 全分布式架构,每个节点上运行一个Midolman进程(跑在JVM上),用于负责从分布式数据库(zookeeper)同步并发布虚拟网络配置信息,因此实现了去中心化,每个节点可以独立计算出转发结果 虚拟拓朴仿真机制,Midolman从VIF上收到报文后,通过拓朴转发仿真(与我们常规的Bridge->Router->Router->Bridge转发类似),就可以计算出目的VIF,从而得知目的虚拟机所在的主机,并在通过Underlay网络的IP以NVGRE/VXLAN的方式将报文Overlay传送给目的主机前就将报文编辑好 内核转发采用OVS datapath,即采用exact match的flow转发(megaflow暂不支持),以实现次包(即首包之后的包)的内核转发,提高转发性能 结合上面的介绍,Midonet采用基于VIF分配的原因就比较清楚的: 即然源端可以直接仿真出目的VIF且完成报文编辑,因此到对端唯一需要做的就是知道对端的VIF对应的OVS datapath接口是什么,NVGRE/VXLAN封装中的Tunnel Key则提供了一个简单方便的编码点,于是midonet就这么用了,另外一个Host上通过Zookeeper分配的Tunnel Key空间有10位数(非标准限制,而是Zookeeper的限制),对于一个cpu有限、memory有限的主机来说,10位数的VM已经远超能力极限的(咱们又不是天河一号,天河一号估计也不行),已经足足够用了 Midonet采用与OVS相似的机制,转件层面采用wildcard match流表,内核采用exact match流表,因此在主机上的Midolman扫描到VIF对应的TAP接口时,Midolman就可以为这个VIF生成一条匹配域为该VIF在该主机上所申请到的Tunnel Key为匹配项的wildcard match流表,其出接口即为该VIF所对应的OVS datapath接口,当收到含有该Tunnel Key的报文时,可以直接命中该wildcard match流表,提取报文字段生成exact match流表出接口继承该wildcard流表向OVS datapath下发即可,Wildcard match流表查询及exact match流表下发会非常迅速,也弥补了Java/Scala代码在未被JIT编译器优化时的性能损失,是一个非常优雅的方案 以上便是我个人对于Midonet Tunnel Key分配的理解,在软件定义网络这股风潮日近的今天,来自于互联网IT企业所带来的新思维,新用法,也许会继续改变设备商的开发模式,设备商要想办法适应并拥抱这种变化了。

January 11, 2015 · 1 min · fortitude.zhang

黄金时代

国庆期间和老婆在看完了稍煽情的“亲爱的”以及速食搞笑式的“心花路放”这两部热门电影后,补了团购的差价冲着文艺片的口号去看了电影“黄金时代”,看了一会儿才发现这原来是讲述作家萧红的传记式电影,我并不熟悉萧红的作品,尽管剧中大部分的作家我都有过耳闻,所以这篇并不是观后感,而是借题发挥,因为如果从黄金宝贵的含义来说,我即将步向三十岁,并同我的爱人结了婚,我生命中最为珍贵而且无忧无虑的生活也即属于我们的“黄金时代”已经划上了句号,因此即便看完电影已经过了三天,我都难以不想起我的黄金时代已经结束这样的一个现实,于是只好在这深夜里,随笔用我刚清理过的机械键盘打下下面这些文字吧。 看电影的过程中听着画外音所转述出的萧红那强烈画面感(或者我应该用即视感这样更为时髦的话语吧,但去他娘的,我就是这么的固执)的文字,我突然意识到,在我黄金时代的前二十几年,我也曾经对文字有过一种说不出的迷恋,而如今近十年过去,我却很少再能够静下心来,读篇散文或者长篇小说,心中柔软的地方已经变的坚硬,少有被触动了,这并不我所期望的一种状态,无论如何,在我未来的生命中,我都想再次疾呼并提醒自己,无论信息多么的碎片,我都应该试着再次软化心中的某块地方,毕竟,人并非机器,尽管人已经逐渐被机器所束缚,我们总需要情感来支撑自己活过。 假期陪老婆看装修材料,体验了老婆平日里跑装饰城的艰辛,看着老婆稍显稀疏的头发,心里涌起一阵阵的伤感,尽管老婆高中毕业,我曾经想去证明既便我们这样的搭配依然能过的很好,如今看来却有些荒唐与可笑了,我谁不需要去证明,也无需给自己证明,老婆同我度过的六年,同样也是她的黄金时代,我们一起笑过、哭过、闹过、打过,如今仍然相依为命在一起,生活也逐渐的稳定起来,我们都彼此都真心的付出过,就不再愧对于自己过往的时代了。 我并不是一个安份的人,既使如今定居在苏州三年,我心里仍一直有些躁动,渴望继续能够在不同的城市生活,老婆也很支持我这样的想法,我很期望这就是我们将来的时代所最明显的印记,时而漂迫,时而安定,永远生活在别处。

October 7, 2014 · 1 min · fortitude.zhang

使用ghostscript合并多个pdf文件

最近用chrome从网上打印了一系列文章,于是弄出来了一堆pdf,很想把这些pdf合并成一个pdf,再编辑下书签水印的,很容易弄的有点像个书的样子。 搜索来发现还是ghostscript这个工具比较适合,gentoo上直接emerge即可,其他发行版应该比gentoo更为简单。 从ghostscript的官方网站的这句介绍的话: Welcome to the Home Page for Ghostscript, an interpreter for the PostScript language and for PDF, and related software and documentation. 可以很容易看出,ghostscript做为PDF解释器,是能够理解PDF的内部构造的,那么合并PDF这样的事情,应该会做的不错。 闲话不多说,安装完ghostscript后,可以用如下命令行来合并一系列的pdf。 gs -dBATCH -dNOPAUSE -q -sDEVICE=pdfwrite -sOutputFile=finished.pdf file1.pdf file2.pdf 简单解释一下上面的选项: -dBATCH 批量处理,做完就乖乖退出 -dNOPAUSE 每页PDF都给我处理好,不要等我再下指令 -q 不必要告诉我你在正确处理 -sDEVICE=pdfwrite 使用内置pdfwrite设备来写文章 -sOutputFile=finished.pdf 合并后的文件名 怎么样,还是比较简单吧? 附: [1] ghostscript官方主页

July 21, 2014 · 1 min · fortitude.zhang

关于产品的思考(一)

最近百度一位30岁的副总引起了关注,在c114上甚至看到了有人发了将其与李一男对比谁更厉害的帖子,然而我对论坛口水战没有兴趣,我更关注是他们均随着成就产品的同时而成就了自己,前者搞火了百度贴吧、百度百科,使百度在移动互联网时代取得了不错的份额,后者则是用CC08程控交换机帮老任打了如今华为的坚实基础,人们需要好的产品。 上次我在京东购买了一条3.5mm的耳机插口延长线,其接口应该采用铜金属制成,直径约一厘米,长约三厘米,直到最近我才注意这样设计的好处,原来在桌子上,由于这么多的铜,使得这个东西对桌子的吸附力要比一般的普通延长线要好的多,也就不容易在听歌时掉到地上去,这款产品于我而言,就是很不错的产品,我如果再有类似的需求,也同样会优先考虑这样的产品。 以往在华为工作时,做为大公司里的一名螺丝钉,如果非要套上一个产品的话,我所负责的模块或者项目,可以称得上产品,交付一个少bug稳定的特性,或许就可以称得上交付了一个产品,然而正如那句经典的交换机上80%的特性都是无用特性的话所说,我不知道我所交付的这个所谓的产品,是否能让用户在用到时,有如同我上面的那样的感觉,而在敏感且对可靠性要求严格的路由器产品上,软件的稳定性尤其重要,这样的特性,即使用户不会使用,也将自此融入产品,再不敢轻易删除,从此软件越来越膨胀。 从华为辞职后,在新公司,因为公司规模的缘故,我与之前相比,更容易接近客户了,那么到底怎么样去衡量客户的需求是否合理,我们做出来的东西是否能让客户产生眼前一亮的感觉,惊叹一声这恰好就是我所期望的东西,如何避免上面软件膨胀的问题,对我而言都是具有挑战性的工作,使我越来越觉得如何评估如何做出好的产品,满足客户需要的产品的重要性了。 对于这样的问题,显然是不会现成的答案的,甚至我觉得,不同的领域,不同的客户,对于好的产品的定义,也自然是不同的,比如对于不爱听歌的人,显然无法给出什么样的随身播放器是一款好的产品。 如果要弄清楚这样的问题,首先就要对目标市场有深入的理解,甚至需要对目标市场的主要玩家的产品有深入的理解,真正了解客户目前在怎么用,是否遇到了什么问题,如果有机会的话,需要和客户做更多的交流,甚至在现场去了解,去分析。 其次,要多增加碰撞的机会,真理越辩越明,在相关人之间要着力创建思辩的氛围,在东西未经讨论之前,尽量要避免做老好人的角色,不能唯唯诺诺。 再次,在客户给予条件的时候,要尽早获得客户的反馈,这与敏捷开发的原理相似,借此可以快速迭代,快速优化,尽快让产品接近客户需求本质,避免走弯路。 如今我比以往更渴望,能够做出一款真正有人广泛应用的产品来,让人们的生活更美好,岂不是一件很有成就感的事情么?

July 14, 2014 · 1 min · fortitude.zhang

AOSA之ZeroMQ阅读笔记

AOSA即《The Architecture of Open Source Application》是本不错的书,这本书的写成本本身也采源了开源社区的协作方式,目前已经出了两部,最新的版本为POSA即《The Performance of Open Source Application》,专注于开源软件的性能。 最近抽时间看了AOSA中关于ZeroMQ的章节,自己先前由于工作需求,简单了解到过ZeroMQ,这次可以借机会读读ZeroMQ创始人亲自写的章节,确实有不少的收获,记录在这里,在后续的项目实践中可以予以参考使用。 Library设计 The lesson here is pretty obvious: Don’t use global state in libraries. If you do, the library is likely to break when it happens to be instantiated twice in the same process. ZeroMQ设计时经过分析和对比,最终采用了Library而非单一的消息服务器的方案,在设计Library时,得出了上述结论。 也即对于Library而言,最好避免全局状态,采用Context的方式较好,特别是存在library被额外的library依赖在同一程序中存在多份library实例时可以避免带来的竞争性问题。 了解真正的问题 There are many more pitfalls in benchmarking the messaging systems that we won’t go further into. The stress should rather be placed on the lesson learned: Make sure you understand the problem you are solving. Even a problem as simple as “make it fast” can take lot of work to understand properly. What’s more, if you don’t understand the problem, you are likely to build implicit assumptions and popular myths into your code, making the solution either flawed or at least much more complex or much less useful than it could possibly be. ...

July 8, 2014 · 3 min · fortitude.zhang