本文介绍Linux进程基础相关知识,供大家学习参考。
计算机实际上可以做的事情实质上非常简单,比如计算两个数的和,再比如在内存中寻找到某个地址等等。这些最基础的计算机动作被称为指令 (instruction)。所谓的程序(program),就是这样一系列指令的所构成的集合。通过程序,我们可以让计算机完成复杂的操作。程序大多数时候被存储为可执行的文件。这样一个可执行文件就像是一个菜谱,计算机可以按照菜谱作出可口的饭菜。
那么,程序和进程(process)的区别又是什么呢?
进程是程序的一个具体实现。只有食谱没什么用,我们总要按照食谱的指点真正一步步实行,才能做出菜肴。进程是执行程序的过程,类似于按照食谱,真正去做菜的过程。同一个程序可以执行多次,每次都可以在内存中开辟独立的空间来装载,从而产生多个进程。不同的进程还可以拥有各自独立的IO接口。
操作系统的一个重要功能就是为进程提供方便,比如说为进程分配内存空间,管理进程的相关信息等等,就好像是为我们准备好了一个精美的厨房。
1. 看一眼进程
首先,我们可以使用$ps命令来查询正在运行的进程,比如$ps -eo pid,comm,cmd,下图为执行结果:
(-e表示列出全部进程,-o pid,comm,cmd表示我们需要PID,COMMAND,CMD信息)
每一行代表了一个进程。每一行又分为三列。第一列PID(process IDentity)是一个整数,每一个进程都有一个唯一的PID来代表自己的身份,进程也可以根据PID来识别其他的进程。第二列COMMAND是这个进程的简称。第三列CMD是进程所对应的程序以及运行时所带的参数。
(第三列有一些由中括号[]括起来的。它们是kernel的一部分功能,被打扮成进程的样子以方便操作系统管理。我们不必考虑它们。)
我们看第一行,PID为1,名字为init。这个进程是执行/bin/init这一文件(程序)生成的。当Linux启动的时候,init是系统创建的第一个进程,这一进程会一直存在,直到我们关闭计算机。这一进程有特殊的重要性,我们会不断提到它。
2. 如何创建一个进程
实际上,当计算机开机的时候,内核(kernel)只建立了一个init进程。Linux kernel并不提供直接建立新进程的系统调用。剩下的所有进程都是init进程通过fork机制建立的。新的进程要通过老的进程复制自身得到,这就是fork。fork是一个系统调用。进程存活于内存中。每个进程都在内存中分配有属于自己的一片空间 (address space)。当进程fork的时候,Linux在内存中开辟出一片新的内存空间给新的进程,并将老的进程空间中的内容复制到新的空间中,此后两个进程同时运行。
老进程成为新进程的父进程(parent process),而相应的,新进程就是老的进程的子进程(child process)。一个进程除了有一个PID之外,还会有一个PPID(parent PID)来存储的父进程PID。如果我们循着PPID不断向上追溯的话,总会发现其源头是init进程。所以说,所有的进程也构成一个以init为根的树状结构。
如下,我们查询当前shell下的进程:
root@vamei:~# ps -o pid,ppid,cmd
PID PPID CMD
16935 3101 sudo -i
16939 16935 -bash
23774 16939 ps -o pid,ppid,cmd
我们可以看到,第二个进程bash是第一个进程sudo的子进程,而第三个进程ps是第二个进程的子进程。
还可以用$pstree命令来显示整个进程树:
init─┬─NetworkManager─┬─dhclient
│ └─2*[{NetworkManager}]
├─accounts-daemon───{accounts-daemon}
├─acpid
├─apache2─┬─apache2
│ └─2*[apache2───26*[{apache2}]]
├─at-spi-bus-laun───2*[{at-spi-bus-laun}]
├─atd
├─avahi-daemon───avahi-daemon
├─bluetoothd
├─colord───2*[{colord}]
├─console-kit-dae───64*[{console-kit-dae}]
├─cron
├─cupsd───2*[dbus]
├─2*[dbus-daemon]
├─dbus-launch
├─dconf-service───2*[{dconf-service}]
├─dropbox───15*[{dropbox}]
├─firefox───27*[{firefox}]
├─gconfd-2
├─geoclue-master
├─6*[getty]
├─gnome-keyring-d───7*[{gnome-keyring-d}]
├─gnome-terminal─┬─bash
│ ├─bash───pstree
│ ├─gnome-pty-helpe
│ ├─sh───R───{R}
│ └─3*[{gnome-terminal}]
fork通常作为一个函数被调用。这个函数会有两次返回,将子进程的PID返回给父进程,0返回给子进程。实际上,子进程总可以查询自己的PPID来知道自己的父进程是谁,这样,一对父进程和子进程就可以随时查询对方。
通常在调用fork函数之后,程序会设计一个if选择结构。当PID等于0时,说明该进程为子进程,那么让它执行某些指令,比如说使用exec库函数(library function)读取另一个程序文件,并在当前的进程空间执行 (这实际上是我们使用fork的一大目的: 为某一程序创建进程);而当PID为一个正整数时,说明为父进程,则执行另外一些指令。由此,就可以在子进程建立之后,让它执行与父进程不同的功能。
3.子进程的终结(termination)
当子进程终结时,它会通知父进程,并清空自己所占据的内存,并在kernel里留下自己的退出信息(exit code,如果顺利运行,为0;如果有错误或异常状况,为>0的整数)。在这个信息里,会解释该进程为什么退出。父进程在得知子进程终结时,有责任对该子进程使用wait系统调用。这个wait函数能从kernel中取出子进程的退出信息,并清空该信息在kernel中所占据的空间。但是,如果父进程早于子进程终结,子进程就会成为一个孤儿(orphand)进程。孤儿进程会被过继给init进程,init进程也就成了该进程的父进程。init进程负责该子进程终结时调用wait函数。
当然,一个糟糕的程序也完全可能造成子进程的退出信息滞留在kernel中的状况(父进程不对子进程调用wait函数),这样的情况下,子进程成为僵尸(zombie)进程。当大量僵尸进程积累时,内存空间会被挤占。
4.进程与线程(thread)
尽管在UNIX中,进程与线程是有联系但不同的两个东西,但在Linux中,线程只是一种特殊的进程。多个线程之间可以共享内存空间和IO接口。所以,进程是Linux程序的唯一的实现方式。
总结:
程序,进程,PID,内存空间
子进程,父进程,PPID,fork, wait
作者:Vamei 出处:http://www.cnblogs.com/vamei
本文介绍Linux文本流相关知识,供大家学习参考。
文件用于数据的存储,相当于一个个存储数据的房子。我们之前说,所谓的数据是0或者1的序列,但严格来说,Linux以字节(byte)来作为数据的单位,也就是说这个序列每八位(bit)为一个单位(八位二进制对应的十进制范围为0到255)。使用ASCII编码,可以将这样一个字节转换成为字符。所以,在Linux中,我们所说的数据,完全可以用字符表达出来,也就是说文本(text)的形式。
实际上,如果以bit为单位处理字符的话,机器会更容易读懂和传输,效率会更高。但为什么Linux依然以字节为单位进行处理呢?原因在于,相对于以bit为单位处理数据,以byte为单位可以更容易将数据转化为字符。相对于枯燥的0和1,字符更容易被人读懂 (human readable)。然而,并不是所有的数据都是设计来让人读懂的,比如可执行文件包含的各种字符对于人来说并没有什么意义 (因为可执行文件是为了让机器读懂的)。但Linux依然以字节为单位处理所有文件,这是为了让所有文件能够共用一套接口 (virtual file system),从而减少Linux设计的复杂度。
("everything is a file"是通常所流传的UNIX设计的哲学之一,但Linus对此作出纠正,改为"everything is a stream of bytes"。)
然而,数据不是在找到了自己的房子(file)之后就永远的定居下来。它往往要被读入到内存 (就像是到办公室上班),或者被传送到外部设备(好像去酒店休假),或者搬到别的房子中。在这样的搬迁过程中,数据像是一个个排着队走路的人流,我们叫它文本流(text stream,或者byte stream)。然而,计算机不同设备之间的连接方法差异很大,从内存到文件的连接像是爬山,从内存到外设像是游过一条河。为此,Linux还定义了流 (stream),以此作为修建连接各处的公路的标准。Stream的好处在于,无论你是从内存到外设,还是从内存到文件,所有的公路都是相同的 (至于公路下面是石头还是土地,都可以不用操心)。
我们再回味一下“everything is a stream of bytes”这句话。信息包含在文本流中,不断在计算机的各个组件之间流动,不断地接受计算机的加工,最终成为用户所需要的某种服务。
(说句题外话,如果看过骇客帝国的话,一定会对文本流印象深刻。)
标准输入,标准输出,标准错误与重新定向
当Linux执行一个程序的时候,会自动打开三个流,标准输入(standard input),标准输出(standard output),标准错误(standard error)。比如说你打开命令行的时候,默认情况下,命令行的标准输入连接到键盘,标准输出和标准错误都连接到屏幕。对于一个程序来说,尽管它总会打开这三个流,但它会根据需要使用,并不是一定要使用。
想象一下敲击一个
$ls
键盘敲击的文本流("ls\n",\n是回车时输入的字符,表示换行)命令行 (命令行实际上也是一个程序)。命令行随后调用/bin/ls得到结果("a.txt"),最后这个输出的文本流("a.txt")流到屏幕,显示出来,比如说:
a.txt
假设说我们不想让文本流流到屏幕,而是流到另一个文件,我们可以采用重新定向(redirect)的机制。
$ls > a.txt
重新定向标准输出。这里的>就是提醒命令行,让它知道我现在想变换文本流的方向了,我们不让标准输出输出到屏幕,而是要到a.txt这个文件 (好像火车轨道换轨)。此时,计算机会新建一个a.txt的文件,并将命令行的标准输出指向这个文件。
有另一个符号:
$ls >> a.txt
这里>>的作用也是重新定向标准输出。如果a.txt已经存在的话,ls产生的文本流会附加在a.txt的结尾,而不会像>那样每次都新建a.txt。
我们下面介绍命令echo:
$echo IamVamei
echo的作用是将文本流导向标准输出。在这里,echo的作用就是将IamVamei输出到屏幕上。如果是 $echo IamVamei > a.txt
a.txt中就会有IamVamei这个文本。
我们也可以用<符号来改变标准输入。比如cat命令,它可以从标准输入读入文本流,并输出到标准输出:
$cat < a.txt
我们将cat标准输入指向a.txt,文本会从文件流到cat,然后再输出到屏幕上。当然,我们还可以同时重新定向标准输出:
$cat < a.txt > b.txt
这样,a.txt的内容就复制到了b.txt中。
我们还可以使用>&来同时重新定向标准输入和标准错误。假设我们并没有一个目录void。那么 $cd void > a.txt 会在屏幕上返回错误信息。因为此时标准错误依然指向屏幕。当我们使用:
$cd void >& a.txt
错误信息被导向a.txt。
如果只想重新定向标准错误,可以使用2>:
$cd void 2> a.txt > b.txt
标准错误对应的总是2号,所以有以上写法。标准错误输出到a.txt,标准输出输出到b.txt。
管道 (pipe)
理解了以上的内容之后,管道的概念就易如反掌。管道可以将一个命令的输出导向另一个命令的输入,从而让两个(或者更多命令)像流水线一样连续工作,不断地处理文本流。在命令行中,我们用|表示管道:
$cat < a.txt | wc
wc命令代表word count,用于统计文本中的行、词以及字符的总数。a.txt中的文本先流到cat,然后从cat的标准输出流到wc的标准输入,从而让wc知道自己要处理的是a.txt这个字符串。
Linux的各个命令实际上高度专业化,并尽量相互独立。每一个都只专注于一个小的功能。但通过pipe,我们可以将这些功能合在一起,实现一些复杂的目的。
总结:
文本流,标准输入,标准输出,标准错误
cat, echo, wc
>, >>, <, |
作者:Vamei 出处:http://www.cnblogs.com/vamei
本文介绍Linux文件管理背景知识,供大家学习参考。
对于计算机来说,所谓的数据就是0和1的序列。这样的一个序列可以存储在内存中,但内存中的数据会随着关机而消失。为了将数据长久保存,我们把数据存储在光盘或者硬盘中。根据我们的需要,我们通常会将数据分开保存到文件这样一个个的小单位中(所谓的小,是相对于所有的数据而言)。但如果数据只能组织为文件的话,而不能分类的话,文件还是会杂乱无章。每次我们搜索某一个文件,就要一个文件又一个文件地检查,太过麻烦。文件系统(file system)是就是文件在逻辑上组织形式,它以一种更加清晰的方式来存放各个文件。
1. 路径与文件简介
文件被组织到文件系统(file system)中,通常会成为一个树状(tree)结构。Linux有一个根目录\, 也就是树状结构的最顶端。这个树的分叉的最末端都代表一个文件,而这个树的分叉处则是一个目录(directory, 相当于我们在windows界面中看到的文件夹)。在图1中看到的是整个的一个文件树。如果我们从该树中截取一部分,比如说从目录vamei开始往下,实际上也构成一个文件系统。
要找到一个文件,除了要知道该文件的文件名,还需要知道从树根到该文件的所有目录名。从根目录开始的所有途径的目录名和文件名构成一个路径(path)。比如说,我们在Linux中寻找一个文件file.txt,不仅要知道文件名(file.txt),还要知道完整路径,也就是绝对路径(/home/vamei/doc/file.txt)。从根目录录\, 也就是树状结构的最顶端出发,经过目录home, vamei, doc,最终才看到文件file.txt。整个文件系统层层分级(hierarchy),vamei是home的子目录,而home是vamei的父目录。
在Linux中,我们用ls命令来显示目录下的所有文件,比如 $ls /home/vamei/doc
图1 文件树
(如该图中所示的文件系统,即绿色构成的树。最顶端的根目录(\),沿红色箭头标出的路径,我们最终找到文件file.txt。)
2. 目录
在Linux系统中,目录也是一种文件。所以/home/vamei是指向目录文件vamei的绝对路径。
这个文件中至少包含有以下条目:
. 指向当前目录
.. 指向父目录
除此之外,目录文件中还包含有属于该目录的文件的文件名,比如vamei中就还要有如下条目,指向属于该目录的文件:
doc
movie
photo
Linux解释一个绝对路径的方式如下:先找到根目录文件,从该目录文件中读取home目录文件的位置,然后从home文件中读取vamei的位置……直到找到目录doc中的file.txt的位置。
由于目录文件中都有.和..的条目,我们可以在路径中加入.或者..来表示当前目录或者父目录,比如/home/vamei/doc/..与/home/vamei等同。
此外,Linux会维护一个工作目录(present working directory)的变量。你可以随时查询到到工作目录(在命令行输入$pwd)。这是为了省去每次都输入很长的绝对路径的麻烦。比如说我们将工作目录更改为/home/vamei ($cd /home/vamei),那么此时我们再去找file.txt就可以省去/home/vamei/ ($ls doc/file.txt),这样得到的路径叫相对路径(relative path),上面的doc/file.txt就是这样一个相对路径。
3. 文件操作
对于文件,我们可以读取(read),写入(write)和运行(execute)。读取是从已经存在的文件中获得数据。写入是向新的文件或者旧的文件写入数据。如果文件储存的是可执行的二进制码,那么它可以被载入内存,作为一个程序运行。在Linux的文件系统中,如果某个用户想对某个文件执行某一种操作,那么该用户必须拥有对该文件进行这一操作的权限。文件权限的信息保存在文件信息(metadata)中, 见下一节。
4. 文件附加信息 (metadata)
文件自身包含的只有数据。文件名实际上储存在目录文件。除了这些之外,还有操作系统维护的文件附加信息,比如文件类型,文件尺寸,文件权限,文件修改时间,文件读取时间等。可以用ls命令查询文件信息($ls -l file.txt),得到如下结果:
-rw-r--r-- 1 vamei vamei 8445 Sep 8 07:33 file1.txt
我们先介绍最开始的-,它表示文件类型,说明file1.txt是常规文件(如果是目录文件,则应显示d)。
随后有九个字符,为rw-r--r--,它们用于表示文件权限。这九个字符分为三组,rw-, r--, r--,分别对应拥有者(owner),拥有组(owner group)和所有其他人(other)。回顾Linux开机启动,登录后,我会有一个用户身份和一个组身份, 相当于我的名片。第一组表示,如果我的名片上的用户身份证明我是该文件的拥有者,那么我就可以对该文件有读取(r),写入(w)该文件的权限,但不拥有执行(-,如果拥有执行权限,则为x)该文件的权限。第二组表示,如果我的名片上的组身份证明我所在的组是该文件的拥有组的一员,那么我有从该文件读入的权限。第三组表示,如果我的名片显示我既不是拥有者,也不是拥有组的一员,那么我只有读入的权限。当我想要进行一个读取操作时,Linux会先看我是否是拥有者下文会进一步解释拥有者和拥有组。
后面的1是硬连接(hard link)数目(link count)。当file1.txt出现在目录文件/home/vamei/doc中时,那么就建立了一个硬链接。file1.txt是允许属于多个目录文件,相当于同一个文件出现在多个目录下,也就是说link count可以不止是1。当link count = 0的时候,相当于文件脱离了文件树,操作系统就会删除该文件。所以很多时候unlink与remove在操作系统中是一个意思。由于软链接(soft link)的广泛使用(soft link不会影响link count,而且可以跨越文件系统),现在较少手动建立硬连接。
之后的vamei表示用户vamei是文件的拥有者(owner),文件的拥有者有权更改文件权限(比如改为rwxrwxrwx)。而后面的vamei文件的拥有组是组vamei。文件的拥有者和拥有组在文件创建时就附加在文件上(相当于给文件上锁,只有有合适名片的用户才能打开操作)。要注意,Linux有一个超级用户root (也叫做根用户),该用户拥有所有的文件。
随后的8445表示文件大小,单位为byte。
Sep 8 07:33表示文件的上一次写入的时间(modification time)。实际上在文件附加信息中还包含有文件的上一次读取时间(access time),没有显示出来。
5. 软链接
如上所说,软链接不会影响文件的link count。如果还记得windows系统的快捷方式的话,linux的软链接(soft link,也叫做symbolic link)就是linux的快捷方式。软链接本质上是一个文件,它的文件类型是symbolic link。在这个文件中,包含有链接指向的文件的绝对路径。当你从这个文件读取数据时,linux会把你导向所指向的文件,然后从那个文件中读取(就好像你双击快捷方式的效果一样)。软链接可以方便的在任何地方建立,并指向任何一个绝对路径。
软链接本身也是一个文件,也可以执行文件所可以进行的操作。当我们对软链接操作时,要注意我们是对软链接本身操作,还是对软链接指向的目标操作。如果是后者,我们就说该操作跟随链接指引(follow the link)。
总结
计算机本质上是对数据进行处理的工具,而文件是数据储存的逻辑载体,所以了解Linux文件系统很重要。对于文件系统的了解要结合Linux的其他方面(比如用户管理)进行有机的学习。
文件权限,拥有者,拥有组,超级用户root。
硬链接,软链接,follow the link。
作者:Vamei 出处:http://www.cnblogs.com/vamei