时代的印迹:Unix操作系统发展历程

UNIX系统的起源远远早于现在的主流系统windows,说起Unix就要追本溯源,看看二十世纪六十年代计算机刚刚开始大规模应用的情况。当时的计算机课没有现在那么强悍,当时是真正的计算机,而且采用的还是批处理……所以呢当时的好几家大公司,比如通用电气公司、美国电话电报局还有著名的麻省理工学院和贝尔实验室打算联合开发一个多用途操作系统,名字就是MULTICS,可惜的是目标太多没有完成,所以到1969年贝尔实验室退出了这个项目。

虽然贝尔实验室退出了,但是实验室内一个叫Ken Thompson的人可不愿意了。此公当时给MULTICS写了一个游戏,叫“Space Travel”只可惜在当时半成品的MULTICS上运行起来很慢……所以这哥们为了让自己写的游戏能玩就写了一个极为简单的操作系统,当时被同事们嘲笑为Unics(对MULTICS的一种戏称)所幸,此公就把自己写的操作系统命名为UNIX。

1971年UNIX发布了第一个版本,该系统占用12KB内存,以现代的眼光来看是非常迷你了呢。

1973年UNIX发布第三版的时候,Ken Thompson和Dennis Ritchie两人感觉用汇编语言移植是在过于头痛,所以尝试用高级语言来编写UNIX,这在当时是不可想象的。为了写Unix他们整合了BCPL语言成型了B语言,可惜B语言还是不能完成编写UNIX的重任,所以又改写了B语言,这就形成了大名鼎鼎的C语言。用C语言编写的UNIX相当的容易移植,于是C和UNIX很快的主导了全球。

然而到1978年,Unix阵营竟然发生了分裂,分裂成了BSD UNIX和AT&T UNIX两大阵营,各自的主流是4.x BSD和system IV

此后UNIX陷入了长期的分裂与动荡之中,比如Bill Joy以BSD为基础创立了Sun Microsystems发展出了Solaris系统,当然还有很多其他版本,比如著名 MAC OS X,freeBSD,OpenBSD等等

此后UNIX更是陷入了windows、Mac osLinux的交替进攻当中,在今天的PC世界桌面版的UNIX几乎绝迹,只有SUN的Solaris还能算是Unix后裔存在,MAC OS X则早已偏移了UNIX的基础。目前Unix的主要领域和linux类似,但是更偏向于高端行业,所以掌握了Unix才算是真正掌握了计算机高端技术,不过对我们平时而言是用不到的。

为什么字节码+虚拟机的模式比机器码+编译器更流行?

众所周知现在的软件行业是解释型语言的天下,无论是开源轻量级语言Python/Perl/Ruby还是企业级应用Java甚至微软新搞出来的.Net系列/C#,都基于字节码+虚拟机模式。这么做毫无疑问比机器码+编译器更慢,而且机器码+编译器也并非不能实现高等语言,那么why?

Hacks and Painters里给出的解答是:字节码十分接近机器码,因此为字节码编写基于硬件的解释器比编写编译器更容易!

想象一下。接近自然语言的高等语言+编译器+机器码与接近机器语言的字节码+解释器+机器码。当然是后者更容易,但是后者就额外需要把源代码翻译成字节码的编译器,但这个编译器只要实现一次就够了!(最复杂的部分只要做一次就够),而针对不同的硬件编写解释器是相对十分容易的。

另外如Hacks and Painters所说的,增加软件中间层,会降低执行效率,但能使编程更灵活,开发出的东西更强大。换句话说,这是一个不断抽象,不断接近人类自然思维的过程。

字节码更通用,更灵活,更强大,不拘于平台限制。其唯一的缺点是降低代码运行效率,但是在硬件速度18个月翻一番的今天,这并不重要。

程序猿

matlab代码求圆周率的简单算法

说起圆周率的算法很多人都会想起一大堆的无穷级数等各种表达式,但是这样的算法需要比较高的数学推理水品,而且对于很多初学者而言很难理解。程序员不能仅仅是机械的写程序,必须要真正的理解程序中每个代码的意义,这样才能像写文章一样写出好程序来,下面介绍一种运算量很大但是却非常简单的圆周率算法。

先问一个问题,圆周率是什么?

可能有人会说是圆周长和直径的比,或者有人说圆面积和半径平方的比。我的一个老师曾经说不要为了复杂而复杂,抓住问题的本质往往是最有效的手段,从圆周率的本质而言,它不是什么无穷级数的收敛值或者某个函数的极限值,它只是我们一个简单的面积比或者长度比。

此外就是,无论怎么计算,圆周率是算不完的,所以必须有无限高的精度才能把圆周率准确的算出来,这样虽然很麻烦却给了我们一个很大的舞台,只要我们这个算法在无限次运算后理论上能得到圆周率精确值就说明算法还是可行的(虽然要考虑效率的问题)

下面说我的算法,以半径1画个圆,然后用一定的步长把圆的外接正方形内部分用点阵的方法扫描出来,这样就可以很轻松的计算每个点是不是在圆内,圆内点的数目和总数目的比就是圆周率的1/4

程序实现起来很简单,下面是matlab的代码(C语言orC++的自己写吧,很简单的)

x=-1:.00003:1;
y=x;
number=0;
number_all=length(x)*length(y);
for i=1:length(x)
    for j=1:length(y)
        if x(i)*x(i)+y(j)*y(j)<1
            number=number+1;
        end
    end
end
pai=4*(number/number_all)

用这个matlab代码的方法算出来的圆周率是3.141560962与3.141592654的公认值还是有比较大的差距的,但是只要提高X的扫描范围就能极大地提高精度,可惜本人的电脑不行,就只能算到这个地步了。

C语言

C Primer Plus里没有提及的C语言scanf()函数用法

根据beginning linux programing 4th edtion发现C语言scanf()函数还有这样的用法:

char str[80];
scanf("%[^\n]",str);

直到读取到回车键才停止读取,而不是像通常的scanf(“%s”,str);那样读到空白字符就停止读取。换言之上述方法可以读取一个句子而不是通常的只能读取一个单词。

不过缺点也是显而易见的,和gets相仿,这样可能导致溢出漏洞。正如,历史上对scanf系列函数的评价并不高。

#include 
int main(void)
{
    char str[80];
    puts("Please enter some word, end with the key ENTER");
    scanf("%[^\n]", str);
    printf("What you input is %s", str);
    return 0;
}
C语言

关于C语言switch

关于C语言switch这个奇葩的东东。

很多C语言习题中都会有输入考试成绩,判断等级(90-100优,80-89良……0-59不及格)的题目。

首先,刚开始学到分支结构会用if … else if … else if …… else这样的结构,例如:

        if (0 = g) 
            GP[n] = 0; 
        else if (60 = g) 
            GP[n] = 1; 
        else if (70 = g) 
            GP[n] = 2; 
        else if (80 = g) 
            GP[n] = 3; 
        else GP[n] = 4;

然后呢,又学到switch,则会变成,先除以10,利用趋零截尾的性质,然后把0-5直接合并了……例如:

          ch = (int)(g / 10); 
            if (ch < 6) 
            { 
                ch = 5; 
            } 
            switch(ch) 
            { 
                case 5 : GP[n] = 0;break; 
                case 6 : GP[n] = 1;break; 
                case 7 : GP[n] = 2;break; 
                case 8 : GP[n] = 3;break; 
                case 9 : GP[n] = 4;break; 
                case 10 : GP[n] = 4;break; 
            }

但是呢,其实呢,这个if再把0-5合并 是可以去掉的,利用switch的特性,结果是这样子:

    switch(grade/10)
    {
        case 0 :
        case 1 :
        case 2 :
        case 3 :
        case 4 :
        case 5 : rank = 'E';
                 break;
        case 6 : rank = 'D';
                 break;
        case 7 : rank = 'C';
                 break;
        case 8 : rank = 'B';
                 break;
        case 9 :
        case 10: rank = 'A';
                 break;
        default: puts("Invalid grade");
                 exit (1);
    }

记得在expert C programing中,作者就很BS当初C语言把switch中,每个分支都要加上break;跳出才不致于执行完所有语句。根据作者统计,97%的使用了switch判断分支的代码都是要用上break;的,也是就是说“完全的选择”,而只有剩下的少得可怜的switch会出现不带break;的情况,所以当时sun的编译器会对switch case不带上break;作waning。但是这里或许正是switch不带break;时的用途所在吧。

其实个人看来,使用switch时加上break;已是非常自然的,不加上break;反而觉得有点怪怪的。正如文中所述,前面两种想法是3月的时候做ACM OJ的模拟题时想到的,而第三种方法还是这两天逛论坛时无意才想起来……

不过,话说回来,这个例子是特例,其实在绝大多数时候,我严重地支持expert C programing的作者的。

至于第三种方法效率是否有所提高,我也不得而知。第一种我个人是比较反对的,原因是多重if else判断让人眼花。通常遇到多重分支判断都会使用switch。其实在本文中,第二第三种应该是不相上下的。