2014年6月 ’ 的文章存档

为什么程序员的开发项目总是半途而废?

很多程序员的项目常常半途而废。他们有那么多的好点子,但是很多都流于空想。几乎每一个软件开发者都有一个这样的文件夹,里面很多都是些还没完工的项目,而这些程序里有不少在它诞生初期真心是个超棒的点子。和这些人一样,我也有很多好主意,有的甚至就是现在有些企业在用的。比如正决定着在eBay上还是在Amazon上做电子商务获利、做一个以业务为基础的社交网络(水管业、电子行业、软件开发等)、比特币搜索引擎、开发一个CSS框架来取代Bootstrap、从Instagram上找出最有魅力的那些人、开发一个实时访问统计引擎,这样的例子举不胜举。几乎每一个项目我都着手去做了,但是没见到一个是完工了的。当我和我的同事朋友聊起这个话题的时候,他们也普遍反映,他们也是如此。很多很多的好点子都夭折在他们的文件夹里。为什么会这样?

因为成功所以缺乏动力

首先要谈谈我们的职业,拥有着让人眼红的高就业率。用事实说话,全国的失业率约为6.7%,而WEB开发员的失业率还不到1%。而且我们的工资水平也比平均值高。2012年软件开发员的平均年收入就已经超过9万美元。当然现在你要是足够优秀,完全可以远远超过这个数。看着跟着我学习的新手菜鸟成为入门级程序员,心中有一种莫大的个人成就感。而这个职业的薪酬也让他们很满意:起薪4万5千美元到7万美元。

所以,和其他大多数人相比,特别是那些在工作岗位上碌碌无为做一天和尚撞一天钟的,因为岗位薪资不高而不断跳槽的,软件开发员还是比较成功的。这里请允许我冒昧的做一个概括,如有雷同纯属巧合。可以说,半路出家的程序员相对于这些人是比较成功的了。也就是这种成功的优越感麻痹了我们的神经,使得我们缺少了完成目标的动力。
阅读全文

统治世界的十大算法

a2

软件正在统治世界。而软件的核心则是算法。算法千千万万,又有哪些算法属于“皇冠上的珍珠”呢?Marcos Otero 给出了他的看法

什么是算法?

通俗而言,算法是一个定义明确的计算过程,可以一些值或一组值作为输入并产生一些值或一组值作为输出。因此算法就是将输入转为输出的一系列计算步骤。

—Thomas H. Cormen,Chales E. Leiserson,算法入门第三版
阅读全文

DNA的三维可视化:通过OpenGL实现一个DNA链

我最近在Coursera上开始了一门课程(数据库和算法,顺便提一句,这是普林斯顿大学开设的一门很牛的课程)。现在上了几次课,我发现这些编程课都相当困难,我的(自我缓解编程困难的)方法则是在OpenGL上编一些小东西来减压。

做三维模型编程最棒的一点就是你可以看到你代码的成果(这也是编程的一个动机)。3D编程是一个很强大的可视化工具,通过编码实现DNA的结构是一个有趣并且富有挑战性的任务。其具有挑战性主要是因为OpenGl代码所带来的局限性。我相信DNA结构在数学方面的实现是很简单的。
阅读全文

推荐几本 Unix/Linux 经典书

几天前答应过一位新 VPS 客户,推荐一点 Linux 书,今天静下来写文才发现推荐书其实不是这么容易,至少应该知道一点读书人的背景,是入门、进阶还是高级,阅读习惯啊、方式啊;有人喜欢看例子书,边看边操作学得快;有的人喜欢先读原理,后操作。

现在每年增加的信息量比过去几百年还要多,不过好书还是那么少,经过历史考验的经典书就更少了,每个领域的经典书就那么几本,作为初学者应该首先看完该领域的经典书,然后再看其他的流行好书。

选择经典书是因为,这个拇指就能陌陌的年代我们实在没有时间花在烂书上,好书能更快更好的帮助我们理解内容,好书是一种享受,你不会觉得读书是一种辛苦。选择英文书是因为,这些经典书的英文都写得朴实、简单,不超过大学四级的阅读水平,长时间阅读英文书可以为自己以后学习和工作打好基础。在工作中,最新最好最权威的资料都是英文的,翻译过来的二手资料不放心,做技术这行迟早要用英语混日子,老板想派你出国培训、开会你不能腼腆的把机会让给别人吧~
阅读全文

超赞!设计师完全自学指南

designer-self-study-banner

 

本文译自国外高质量问答社区Quora,原文作者Karen X. Cheng,原是微软Excel的项目经理,后通过自学转型成为设计师。她讲述的自学过程详实细致且条理有序,读完会发现与想象中的大不一样,对于想自学设计但迷茫不知道从何入手的童鞋来说,可以遵循她的步骤去学习,除了规划得当,还能对设计有一个全局的了解 : )

阅读之前来看看国内的18岁天才少年如何自学成才建立他的个人网站

设计师完全自学指南:

我不是毕业于设计学系,但我得到了一份设计师的工作。
阅读全文

redis超时问题分析

Redis在分布式应用中占据着越来越重要的地位,短短的几万行代码,实现了一个高性能的数据存储服务。最近dump中心的cm8集群出现过几次redis超时的情况,但是查看redis机器的相关内存都没有发现内存不够,或者内存发生交换的情况,查看redis源码之后,发现在某些情况下redis会出现超时的状况,相关细节如下。

1. 网络。Redis的处理与网络息息相关,如果网络出现闪断则容易发生redis超时的状况。如果出现这种状况首先应查看redis机器网络带宽信息,判断是否有闪断情况发生。

阅读全文

grunt快速上手

这篇文章的目标是帮助大家快速上手grunt,适用的grunt版本为0.4.x,本文只是大致介绍,如果想做深入了解请阅读grunt官方文档

安装grunt命令行工具

首先确保你的node版本在0.8以上(暂时不建议适用0.10.0),命令:

node -v

然后安装grunt命令行工具grunt-cli

npm install -g grunt-cli

可能需要前面加上sudo(例如 OSX, *nix)。

如果你只前装过grunt的老版本的话则需要卸载:

npm uninstall -g grunt

安装grunt模板

grunt模板的作用是帮助你生成初始的Gruntfile.js文件,当然你也可以直接把其它项目的Gruntfile.js文件拷贝过来使用。

首先要安装命令行工具grunt-init:

npm install -g grunt-init

然后安装模板,目前有三种模板是由grunt官方做维护的,还有别的可在github上找到,或者你自己实现一个。 官方模板的安装命令如下:

然后安装模板,目前有三种模板是由grunt官方做维护的,还有别的可在github上找到,或者你自己实现一个。 官方模板的安装命令如下:

git clone git://github.com/gruntjs/grunt-init-gruntfile.git $HOME/.grunt-init/
git clone git://github.com/gruntjs/grunt-init-jquery.git $HOME/.grunt-init/
git clone git://github.com/gruntjs/grunt-init-node.git $HOME/.grunt-init/

三种分别对应默认grunt模板,jquery插件的grunt模板,node包的grunt模板。

然后就可以适用grunt-init命令来初始化你的Gruntfile.js文件了,例如你要安装默认模板:

grunt-init grunt-init-gruntfile #最后一个参数也可以是模板所在的文件夹

它会问你一些问题,然后根据你的答案创建当前项目的Gruntfile.js文件。

安装所需node包

首先是创建package.json文件(npm的包管理配置文件)。
你可以使用npm init命令创建这个文件或者直接拷贝其它项目的。

然后在项目的根目录下安装你开发依赖的各种grunt包,首先是grunt:

npm install grunt --save-dev

--save-dev可以将你所安装的包自动保存到package.json文件中的devDependencies属性中去,如果你的项目使用时(不仅仅是开发)也需要用到某个包,
你应该使用--save将其保存在dependcies属性中。

你肯定要用到需要其它grunt模块来帮你完成构建任务,这时你就可以用这种方法把他们都加到项目中,例如最常用的concat jshint uglify模块:

npm install grunt-contrib-concat --save-dev
npm install grunt-contrib-jshint --save-dev
npm install grunt-contrib-uglify --save-dev

还有种方式就是先把依赖的包写到package.json中的devDependencies中去,然后直接使用npm nistall来安装。

编辑Gruntfile.js文件使之工作

我们以本站当前使用的Gruntfile.js为例来了解一下grunt设置中最重要的部分。

var path = require('path');
var snippet = require('grunt-contrib-livereload/lib/utils').livereloadSnippet;

module.exports = function(grunt) {
  // Project configuration.
  grunt.initConfig({
    // Task configuration.
    concat: {
      dist: {
        src: ['javascripts/common.js','javascripts/respond.js'],
        dest: 'javascripts/main.js'
      }
    },
    uglify: {
      options: {
        banner: '/*script for site: http://chemzqm.me*/'
      },
      dist: {
        src: '<%= concat.dist.dest %>',
        dest: 'javascripts/main.min.js'
      }
    },
    cssmin: {
      compress:{
        files:{
          "stylesheets/styles.min.css":["stylesheets/styles.css"]
        }
      }
    },
    jshint: {
      options: {
        curly: true,
        eqeqeq: true,
        immed: true,
        latedef: true,
        newcap: true,
        noarg: true,
        sub: true,
        undef: true,
        unused: false,
        boss: true,
        eqnull: true,
        browser: true,
        node: true,
        globals: {
          jQuery: true
        }
      },
      all: ['javascripts/common.js','Gruntfile.js']
    },
    regarde: {
      livereload: {
        files: ['_posts/*.md', 'javascripts/common.js', 'stylesheets/styles.css', '*.html'],
        tasks: ['default', 'livereload']
      }
    },
    connect: {
      livereload: {
        options: {
          port: 8000,
          middleware: function(connect, options) {
            return [snippet, connect.static(path.resolve(process.cwd(), '_site'))];
          }
        }
      }
    },
    bgShell:{
      jekyll:{
        cmd: 'jekyll'
      }
    }
  });

  // These plugins provide necessary tasks.
  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks('grunt-contrib-concat');
  grunt.loadNpmTasks('grunt-contrib-uglify');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-regarde');
  grunt.loadNpmTasks('grunt-bg-shell');
  grunt.loadNpmTasks('grunt-contrib-connect');
  grunt.loadNpmTasks('grunt-contrib-livereload');

  // Default task.
  grunt.registerTask('default', ['jshint', 'concat', 'uglify', 'cssmin', 'bgShell:jekyll']);
  // start livereload server
  grunt.registerTask('server', ['livereload-start', 'connect', 'regarde']);

};
  • module.exports指向调用该模块时将返回的对象,而当你使用grunt命令的时候这个名为Gruntfile.js的文件就会被grunt找到(0.4.0以上会找Gruntfile.js,而之前是grunt.js,具体的找法也很简单,就是从运行命令的目录开始一直往上找,直到找到为止),然后以grunt对象为参数执行该模块返回的函数。
  • grunt.initConfig方法接受一个Plain Object做为参数,该对象的每个属性名代表一个顶层task名字,而值则是具体的配置。配置任务时要注意不能把子任务名配置成任务要读取的配置属性,例如uglify需要options做为配置属性,这时就不能把子任务名也设置成options。如果任务是multitask则可以为一个任务配置多个子任务,例如配置多个合并文件的任务:
    concat: {
      dist: {
        src: ['javascripts/common.js','javascripts/respond.js'],
        dest: 'javascripts/main.js'
      },  
      dist2:{
        src: ['javascripts/*.js'],
        dest:'javascripts/all.js'
      }   
    },

    这样你就可以通过grunt concat来顺序执行两个合并任务,或者grunt concat:dist只执行dist任务。上面用到的任务都是multitask,具体实践的时候从文档或者源码都可以判断。此外,grunt还支持文件名的模式匹配和模板替换的功能,例如匹配所有js文件可以用*.js,想引用concat:dist任务的dist属性可以用<%= concat.dist.dest %>, 详细的说明还请阅读官方文档

  • grunt.loadNpmTasks负责载入npm模块定义的任务,grunt 0.4.0之后没有了核心任务,所以每个任务都需要开发者写代码载入(还有一个grunt.task.loadTasks方法用来加载指定目录task文件,具体参阅task官方文档)。
  • grunt.registerTask用来注册新的任务,它最简单的形式如下:
    grunt.task.registerTask(taskName, taskList)
    名为taskname的任务出发后,tasklist数组(grunt 0.4.0之前是空格分格的字符串)中的任务将被顺序执行, 比较特殊的是名为default的任务,它将在命令行输入grunt后被调用。grunt.registerTaskgrunt.registerMultiTask还可通过传入函数的方式创建新的任务,具体参阅创建task文档

最后重点介绍一下livereload模块,这个模块分两个任务liveload-startliveload ,前一个任务启动livereload服务,后一个任务负责通知浏览器进行刷新。livereload本身不提供文件的web服务,所以我们需要connect任务插件来提供。然后通过require('grune-contrib-livereload/lib/utils').livereloadSnippet获取到livereload插件提供的中间件函数(实质上就是参数为req, res, next的函数),然后配置到connectmiddleware参数中,这时请求connect服务
(http://localhost:8000) 得到的html就会被动态的加入负责与livereload服务端通讯的一段代码。grunt-regarde模块负责监控文件变化(不用grunt-watch是因为那个获得不了变化的文件名),这里我们根据需要配置成每当文件变化后就执行default任务和livereload任务。最后,配置

grunt.registerTask('server', ['livereload-start', 'connect', 'regarde']);

就可以通过grunt server命令同时启用liveload, connect, 和regard服务(顺序可以任意)

grunt已经开始被越来越多的知名项目所使用了(例如jquery和AngularJS),而且它的任务插件也在不断的扩展之中(例如coffeescript, jade, stylus)。虽说相比Makefile笨拙了一些,但更容易满足团队中各种操作系统下开发的需要。

 

文章来自:segmentfault

linux 内核软中断详解

中断的作用:当一个中断信号到达时,CPU必须停止它当前正做的工作,转而去做中断要求其做的事情。

中断分为同步中断和异步中断两种。

1、同步中断又称异常,是由CPU执行指令时由CPU控制单元产生的。异常又分两种:

(1)、 一种是由程序执行出错造成的,内核通过发送一个unix的信号来处理异常。

(2)、一种是由内核必须处理的异常条件产生的,比如缺页异常,内核执行恢复异常的所有步骤。
2、异步中断,通常我们就叫中断。由其他硬件设备按照CPU时钟信号随机产生。
中断处理程序的一般步骤:
一个中断处理程序的几个中断服务例程之间是串行执行的,并且在一个中断处理程序结束前,不应该再次出现这个中断,所以一般中断处理程序是先禁止该中断,然后处理中断,处理完成后在使能该中断。

有一些中断是可以延迟处理的,这种可延迟中断可以在开中断的情况下执行,执行时允许其他中断抢占他。把可延迟中断从中断处理程序中抽出来有助于使内核保持较短的响应时间。

Linux内核使用三种方法来处理这种可延迟的中断任务:可延迟函数(软中断和tasklets)以及工作队列。工作队列是工作在进程上下文中,可以睡眠,软中断和tasklets 是工作在中断上下文,不可以睡眠。本节只讨论软中断和tasklets。

软中断:
Linux 2.6 版本使用如下几个软中断,不同版本之间略有差异。但一下几个不同版本都包含。

HI_SOFTIRQ=0, 处理高优先级的tasklet

TIMER_SOFTIRQ, 时钟中断相关的tasklet.

NET_TX_SOFTIRQ, 内核把数据报文传送给网卡。

NET_RX_SOFTIRQ, 内核从网卡接收数据报文。

TASKLET_SOFTIRQ, 处理常规tasklet。

低下标代表高优先级。

内核中定义了softirq_vec数组来存放各种软中断。定义如下:

static struct softirq_action softirq_vec[32]__cacheline_aligned_in_smp;

数组元素为 softirq_action,一个元素代码一个软中断。不同的软中断号对应不同的数组的下标。

struct softirq_action
{
void (*action)(struct softirq_action *); //软中断发生时执行软中断的处理函数。
void *data; //软中断的处理函数的参数指针。
};

 
初始化软中断时调用函数 open_softirq().如下代码。

void open_softirq(int nr, void (*action)(struct softirq_action*),void *data)
{
softirq_vec[nr].data = data;
softirq_vec[nr].action = action;

}

 

另外一个跟软中断相关的关键字段是 32 位的 preempt_counte字段,用它来跟踪内核抢占和内核控制路径的嵌套,该字段放在每个进程描述符的 thread_info 字段中。用函数preempt_count()来返回该字段的值。

preempt_count字段
描述
0~7 抢占计数器,记录显示禁用本地cpu内核抢占的次数,值为0时代表内核允许抢占。
8~15 软中断计数器。记录软中断被禁用的次数,0表示软中断被激活。
16~27 硬中断计数器。记录硬中断嵌套的层数。irq_entry()增加它的值,irq_exit()递减它的值。
28

 

当内核明确不允许发生抢占或内核正在中断上下文中运行时,必须禁止内核的抢占功能。为了确定当前进程是否能够被抢占,内核快速检查preempt_counte字段是否等于零。

另一个跟软中断相关的字段是每个CPU都有一个32位掩码的字段

typedef struct {

unsigned int __softirq_pending;

} ____cacheline_aligned irq_cpustat_t;

他描述挂起的软中断。每一位对应相应的软中断。比如0位代表HI_SOFTIRQ.

宏local_softirq_pending()来获取该字段的值。

使用函数raise_softirq()来激活软中断。即把响应的软中断号对应的__softirq_pending中的位置1.表示该软中断被挂起。如果当前CPU不在中断上下文中,唤醒内核线程ksoftirqd来检查被挂起的软中断,然后执行相应软中断处理函数。

内核在如下几个点上检查被挂起的软中断:

1、当调用local_bh_enable()函数激活本地CPU的软中断时。条件满足就调用do_softirq() 来处理软中断。

2、当do_IRQ()完成硬中断处理时调用irq_exit()时调用do_softirq()来处理软中断。

3、当一个特殊内核线程ksoftirq/n被唤醒时,处理软中断。

软中断处理函数详解:

asmlinkage void do_softirq(void)

{

    __u32 pending;

    unsigned long flags;

    /*如果当前处于硬中断中,在硬中断处理函数退出时会调用irq_exit()函数来处理软中断,
      或当前软中断被禁用.所以in_interrupt()返回不为1 就没必要处理软中断,直接返回*/

    if (in_interrupt())

       return;

    /*保持中断寄存器的状态并禁用本地CPU的中断*/

    local_irq_save(flags);

    /*取得当前cpu上__softirq_pending字段,获取本地CPU上挂起的软中断*/

    pending = local_softirq_pending();

    /*如果当前CPU上有挂起的软中断,执行__do_softirq()来处理软中断*/

    if (pending)

    {

        __do_softirq();

}

    /*恢复中断寄存器的状态*/

    local_irq_restore(flags);

}

asmlinkage void __do_softirq(void)

{

    struct softirq_action *h;

    __u32 pending;

    int max_restart = MAX_SOFTIRQ_RESTART; //10

    int cpu;

  /*取得当前cpu上__softirq_pending字段,获取本地CPU上挂起的软中断*/

    pending = local_softirq_pending();

    /*debug 用,不讨论*/

    account_system_vtime(current);

    /*禁止本地cpu的软中断,现在本地cpu上挂起的软中断已经存入pending临时变量中了*/

    __local_bh_disable((unsigned long)__builtin_return_address(0));

    /*debug 用,不讨论*/

    trace_softirq_enter();

    /*取本地cpu id 号*/

    cpu = smp_processor_id();

restart:

    /* Reset the pending bitmask before enabling irqs */

    /*清空本地cpu的__softirq_pending字段*/

    set_softirq_pending(0);

    /*开启本地cpu的硬中断*/

    local_irq_enable();

    /*循环执行被挂起的软中断处理函数。相应的软中断的处理函数存在数组softirq_ver[nr]中的元素 softirq_action->action中*/

    h = softirq_vec;

    do {

       if (pending & 1) {
         h->action(h);
         rcu_bh_qsctr_inc(cpu);
       }

         h++;

         pending >>= 1;

    } while (pending);

    /*禁止本地CPU的硬中断*/

    local_irq_disable();

    /*取本地CPU的__softirq_pending,查看是否还有新的被挂起的软中断并且检查被挂起软中断的次数小于10次,如果条件满足,
      继续处理新的被挂起的软中断*/

    pending = local_softirq_pending();

    if (pending && --max_restart)

      goto restart;

    /*如果有新的挂起的软中断并且处理循环次数已经够了10次,
      唤醒ksoftirq内核线程来处理软中断*/

    if (pending)

      wakeup_softirqd();

    /*debug 用,不讨论*/

  trace_softirq_exit();

    account_system_vtime(current);

    /*使能本地CPU的软中断*/

  _local_bh_enable();

}

 

文章来自:耀洋的博客