我眼中的计算机:硬件、操作系统、应用程序
1.计算机 = 硬件 + 操作系统 + 应用程序
有人说过,计算机世界的绝大部分问题都可以通过分层的方法来解决。从一个程序员的角度,我比较喜欢将计算机分为三层,自底向上分别为:
1) 硬件(Hardware),按照冯氏的结构定义,一个处理器由5个部分构成,分别为:存储器;控制器;运算器;输入设备;输出设备。对于常见的计算机而言,存储器如常见的外存储设备;CPU则包含运算器、控制器和内存储;输入输出(I/O)很好理解,这里就不写了。
2) 操作系统:Operation System,操作系统主要负责管理计算机硬件资源,控制其他程序运行并为用户提供交互操作界面的系统软件的集合。讲的具体一点如进程、线程管理;内存管理;信号量机制;IO管理等。最常见OS的如Windows,又如HP-UX、SUSE、VxWorks(嵌入式)。
3) 应用程序层:Application,应用层,指使用各种不同的编程语言(C、C++、JAVA、Ruby、Python,太多了,写不完)、通过各种开发工具并基于各种操作系统开发的——软件。该部分是大部分程序员工作的一层,也是构成我们丰富多彩的生活最直接的一部分。常见的如Word是软件,QQ亦是软件。
2.那些围绕在我们身边的“计算机”
根据上面的分层,对应一下常见的计算机:
最常见的计算机莫过于个人PC,其硬件部分(主要讲CPU)当前最主流的当属Intel和AMD的产品,操作系统对应的如Windows(现在应该是Windows8了,当然如果自己装Linux也有可能),对于基于Windows的应用程序开发,其IDE如微软提供的Virual Studio, VC 6.0等。APP如Word,还有当前用来写文章的Maxthon。
再如华为公司的ATAE,其CPU主要采用Intel的x84_64,操作系统采用SUSE10 Linux,编译器为gcc/g++,其应用程序如FTP。
在服务器世界里,还有惠普公司的HP-UX、IBM的工作站,其对应的操作系统分别为HP Unix、AIX,编译器分别为aCC与xLC。还有那个曾经的SUN也有对应的产品,这里就不提了。
3.程序员在干啥?
对于一个程序员而言,主要在从事APP层的开发工作,如果主要从事C/C++,最直接接触的当属OS层。所以要写好程序,必须去了解操作系统的机制,不同的操作系统都有着不同的限制和调度机制。首当其冲的编译器的机制必须要去了解,比如gcc和g++,其常用的参数有哪些,每个参数代表什么含义,有什么限制?Makefile如何编写?基于该OS开发的程序如何进行调试?操作系统的信号量机制有哪些?同理,对于JAVA的开发而言,最直接的打交道的应该是JVM,可以理解为一个简单的操作系统,所以必须去了解JVM的调度机制,如内存管理、GC机制如何运作?调试工具如jvisualVM、jMap如何使用?
再深一层,就要了解到硬件的限制,如CPU的调度机制、汇编指令、IO等。比如代码a = a+1和a++转变成汇编指令有什么区别,怎样写才是高效的?HP Unix的aCC编译器,“+u1”参数后对生成的汇编指令有何影响?大尾端和小尾端有什么区别,与网络字节序又有何关系?再如,为什么要使用多线程编程,在编码时引入多线程的目的是什么?
上面主要简单描述了一个程序员眼中的计算机分层,那么对于我们的产品——APP或者叫做“软件”,内部又是怎样构成的呢?
4.软件/程序 是什么?
从技术角度而言,软件在运行态可以理解为一个个的进程,其内部可能有几个线程构成。上过操作系统课的同学应该有两句话印象深刻(上课睡觉,作业抄袭的除外):“进程是拥有资源的最小单位,线程是CPU调度的最小单位。”
画个图吧:
每个运行态的应用程序,可以称其为Process,每个Process对应有一个身份标识(PID)。其内部包含1到N个线程,每个线程也有自己的线程句柄。CPU以线程为单位进行调度,资源如MEMORY, CPU, IO等统一由进程拥有,换句话讲Thread1至Thread N均可以直接访问进程所拥有的资源。因此对于多线程程序而言,其资源存在并发访问的可能性,比如对于同一个变量a,Thread1在进行a = 1的时候,Thread 2可能“同时”在进行a = 2的操作,这个操作的结果就是a的赋值结果不可预知。如果Thread1在前,Thread 2在后,那么a最终等于2,反之a最终等于1。对于类似于a这类资源,我们可以定义其为“临界资源”,对于临界资源的保护,成为多线程编程必须面临而又必须解决的问题。
由于进程拥有着计算机的资源,因而其内部线程可以方便的访问其内部的各种资源。比如对于内存而言,Thread1可以通过地址访问到Process内部相应的内存块,Thread2也可以访问到Process内部的对应地址。
5.程序森林
每个计算机上面都不可能仅仅存在一个Process,运行期间,必然是有各种各样的Process构成,如下图:
但问题来了,如果Process1想访问Process2内部的数据,该如何处理呢?进程与进程间的数据访问,通过进程间通信(IPC)来解决,Linux下常见的进程间通信方式如共享内存、管道、Socket等(参见《Linux环境高级编程》,这里不多写了,后面再详细写)。
进程的构造弄清楚了,那进程/程序究竟在做什么?我理解的,程序无非是对于输入的信息/数据进行一定的处理,然后提供一定的输出。
对于输入的数据,可能是数据库的数据存储、可能是网络上的一串码流、抑或是我们单击了一下鼠标。逻辑处理即是对这些输入进行了一系列的数学运算,比如点击了12306上面的“订票”按钮,后台就要计算当前的余票信息,提交的订票信息,最终告诉你有没有票。“有没有票”即是程序的输出。更广泛意义的讲,输出可能是一个网络消息或修改数据库的某个字段的值(软件的所有输出均限制于计算机世界中,记得当年还曾怀疑过究竟人会不会被计算机病毒感染)。所以有人说了:“程序其实很简单,无非是找个编程语言,写串逻辑,处理下数据,偶尔连下数据库,或跟其它进程通通信”。