爱悠闲 > 《Unix-Linux编程实践教程》读书笔记(八)

《Unix-Linux编程实践教程》读书笔记(八)

分类: Linux命令  |  标签: linux编程,unix,操作系统,读书笔记,linux  |  作者: yongchurui 相关  |  发布日期 : 2014-09-07  |  热度 : 104°

第八章 进程和程序:编写命令解释器sh

1.      进程=运行中的程序

         程序是存储在文件中的机器指令的序列。执行程序是指把机器指令的序列载入到内存然后让处理器逐条执行。一个进程是程序运行时的内存空间和设置。

2.      通过命令ps来学习进程

         1)  进程存在于用户空间,用户空间是存放运行的程序和他们的数据的一部分内存空间

ps命令会列出用户空间中当前所有的进程。ps命令的参数及输出各部分的意思。

         2)ps中列出的系统进程中有大部分并不与具体的终端相连,它们是系统启动的,而并非是用户通过终端启动。

3.      进程管理和文件管理的 对比学习:磁盘中的多个文件,既具有内容也有属性;而内存中的进程也需要内核为之分配空间并记录分配情况、属性。

4.      内存和程序

         1)  进程的概念虽然抽象,但进程却代表着一些非常实际的实体:内存中的一些字节。

         2)  内存的三种模式:内存在系统框图中的内核框和进程框;像数组一样描述的内存;物理硬件内存芯片

         3)  建立一个进程的过程同在磁盘创建文件的过程相似:文件的内容存储自磁盘不同的快中,进程的数据也被存储在内存中零散的块中;文件创建需要存储文件存储块的记录列表,进程同样需要维护类似的分配信息。所以在框图中用小方框描述进程是一种抽象。

5.      shell:进程控制和程序控制的一个工具

         Shell的三大功能:运行程序;管理输入和输出;可编程

6.      shell是如何运行程序的:

         Shell运行的时间轴(默认用户已经登录系统,处在命令提示符下):

         -->用户输入程序

         -->系统新建进程

         -->查找程序文件,载入内存

         -->运行程序直到程序退出

7.      理解shell的运行过程后编写shell程序的工作主要集中在以下三个方面:

         1)  一个程序如何运行另外一个程序:程序调用execvp

                a)      exec1.c中execvp函数的使用;

b)      程序中第二条打印丢失的原因(新的程序替换了原来的程序和数据);

c)      execvp函数就像换脑;

d)      编写带输入提示的shell程序psh1.c(逐个输入shell命令和参数,但同样出现了“换脑”后无法继续执行其他命令的情况)

         2)      如何建立一个新的进程:程序调用fork

fork函数会新建一个进程,复制当前进程到新建进程,fork返回时父进程和子进程都从fork后运行。

         a)      forkdemo1.c:使用fork创建一个进程

b)      forkdemo2.c:多次fork验证fork工作机制

c)      forkdemo3.c:在fork函数返回后更具返回值区分父进程和子进程

         3)  父进程如何等待子进程退出:进程调用wait等待子进程退出

                   a)      wait()做两件事:暂停调用它的进程直到子进程结束时通知,取得子进程结束时传递给exit()的值(通信

                   b)      waitdemo1.c:wait阻塞调用它的进程直到子进程结束;wait返回结束进程的PID(这使得父进程知道是哪个子进程结束了,从而进行不同的后续操作)

                   c)      waitdemo2.c:通信。

-->进程以三种方式之一结束(正行完成任务,程序调用exit(0)或者调用return 0; 进程可能失败,exit传递一个非零值;进程被一个信号杀死)

                   -->wait函数的参数完成子进程退出状态的传递:如果子进程调用exit函数退出,则将exit的返回值存储在该整型变量中;如果是被信号杀死,则将信号存储在给变量中。这个整型变量由三部分组成:8个bit记录退出值,7个bit记录信号序号,1个bit指明发生错误并产生了内核映像。

                   -->wait参数中的整型变量由 sys/wait.h中的宏来检测

         4)  小结:shell如何运行程序

                   shell用fork建立新进程;

                   用exec在新进程中运行用户指定的程序;

                   调用wait等待新进程的结束,wait系统调用从内核获取退出状态和信号序号,以告知子进程是如何结束的。

8.      实现一个shell:psh2.c

         1)  问题:ctrl+c不仅杀死了新建进程而且杀死了psh2进程本身,这是因为键盘信号发送给所有连接的进程

         2)  希望用户能够在一行输入所需要运行的程序名及参数

9.      思考:用进程编程

         1)  函数和进程之间的相似性

                  execvp/exit就像call/return:call和return是结构化编程的基础。函数调用所用到的堆栈几乎是没有限制的,一个被调用的程序还可以调用其他的程序。一个通过fork/exec调用起来的程序可以通过fork/exec调用其他的程序。exec传递的参数必须是字符串,这也正好是支持跨平台所需的。

         2)  全局变量和fork/exec

                   普通的全局变量会破坏封装原则,导致出让人意料的副作用和难以维护的代码。Unix提供了环境变量的方式来实现全局变量,环境变量是一些传递给进程的字符串变量合集,不会有副作用。

10.   exit和exec的其他细节

         1)  进程死亡:exit和_exit

                   exit是fork的逆操作;exit刷新所有的流,调用由atexit和on_exit注册的函数,执行当前系统定义的其他与exit有关的操作,然后调用_exit函数。

                   _exit函数是一个内核操作,主要处理所有分配给这个进程的内存,关闭这个进程打开的文件,释放所有内核用于管理和维护该进程的数据结构。具体包括:

                    -->关闭所有文件描述符和目录描述符

                   -->将进程的PPID设置为init进程的PID

                   -->如果父进程调用wait或waitpid来等待子进程结束,则通知父进程

                  -->向父进程发送SIGCHLD(其实父进程没有调用wait,内核也会发送该信号,该信号的默认处理是忽略)。

                   已经死亡但是没有给exit赋值的进程成为僵尸进程(zombie)。有些系统下ps命令中可以看到这些进程并被标记为defunc。

         2)  exec家族

                   分清各个家族成员的不同之处。

11. 小结

         1)  unix同过将可执行代码载入到进程并执行它来运行一个程序。进程是运行一个程序所需要的内存空间和其他资源的集合。

         2)  进程有自己的一套属性,如同磁盘上的文件一样。内存的三个维度的表示。

         3)  fork完成了什么?exec在新进程中执行程序。wait实现父进程对子进程的等待。

----------------------------------------------------------------------------------------

本文链接http://blog.csdn.net/yongchurui/article/details/27379601

2014.05.28