进程控制,基本就是进程创建,执行程序和进程终止。以及进程控制原语和从系统角度了解进程控制。
首先要搞清楚这些id都是什么鬼。
fork调用一次,返回两次:(1)进程可有多个子进程,没有一个函数能够使进程获得其所有子进程的进程ID;(2)一个进程只有一个父进程,可以通过getppid()得到,故返回0给子进程就行。
子进程是父进程的副本,如何理解“副本”?
例如,子进程获得父进程数据空间,堆和栈的副本。注意,这是子进程所拥有的副本。父进程和子进程并不共享这些存储空间部分。父进程和子进程共享正文段。
基于上面这段话,我的理解:(1)副本 != 共享。副本的意思就是,子进程从父进程那里“抄了”一份,相当于两张纸上写了同样的内容,连笔记都一样,复印的道理。但是,后文要提到的“写时复制”技术,让子进程并没有在创建之时就理解全部“复印”父进程的数据段和堆栈,而是“延缓”到父子进程其中一个试图对原来的数据段和堆栈进行更改的时候,再复制。OK,这样讲很明白了。(2)至于程序的正文段,没关系,本来就是只读的,也没有必要搞两份,当然就共享。(3)至于后文会见到的exec,估计是连正文段都一起替换掉了。
linux提供了clone系统调用,允许调用者控制哪些部分由父子进程共享。这里有必要去man一下。
Unlike fork(2), clone() allows the child process to share parts of its execution context with the calling process, such as the memory space, the table of file descriptors, and the table of signal handlers. ……..
例子程序结果,以及对fork和IO的讨论:
write函数是不带缓冲的,所以在fork之前调用,就立即输出了(程序故意没有输出’\n’)。标准IO库(printf函数)是带缓冲的,还需要区别行缓冲(终端设备,只要遇到‘\n’换行符就清空缓冲区输出。既然在fork前就清空缓冲区了,所以fork后,子进程就不会有缓冲区的副本,就不会输出“before fork”)和全缓冲(重定向到文件了,一直将数据缓冲到程序退出,再一并输出。所以在fork的时候,子进程就拿到了缓冲区数据的副本,也会输出一行“before fork”)。
fork之后,父子进程还共享了打开的文件。其原理就是子进程拿到了父进程的进程表项,其中保存着fd标志和文件指针,于是父子进程指向同样的文件表。
子进程继承了父进程的哪些属性?P185.
fork有以下两种用法:
- 一个父进程希望复制自己,使父子进程同时执行不同的代码段。
- 一个进程要执行一个不同的程序。
简言之就是,不复制!(也不是完全不复制,例子中,堆栈和数据都没有复制,但是文件表项是复制了。)
vfork保证子进程线运行。并且,子进程要调用exec或exit之后,父进程才被调度。
在UNIX系统中,_exit和_Exit同义,并不冲洗IO流。
信号可由进程自身,其它进程或内核产生。
不论进程如何终止,最后都会执行内核中同一段代码。这段代码为相应进程关闭所有打开的描述符,释放它所使用的存储器等等。
僵死进程:一个已经终止,但是父进程尚未对其善后处理(获取终止子进程的有关信息,释放它仍然占有的资源)的进程。
init进程的子进程(创建或者收养)终止后不会产生僵死进程。
在我linux上的结果:
有时候需要让父子进程同步,其实这就是进程间同步(有可能是互相告知自己的进度,做了哪些事),一个很常见的问题。子进程可以使用轮询的方式,判断父进程是否终止,但是这种“轮询”技术很浪费时间阿。
为避免竞争条件和轮询,在多个进程之间需要某种形式的信号发送和接受的方法。各种形式的IPC也可以用。
exec并不创建新进程,所以前后PID不变。exec只是用磁盘上的一个新程序替换了当前进程(一般是fork之后的进程,亦可以在进程中直接调用exec)的正文段,数据段,堆段和栈段。
用fork可以创建新进程,用exec可以初始执行新的程序(调度是内核的事情)。
进程的基本控制原语:
- fork,创建新的进程
- exec,初始新的进程
- exit,进程终止
- wait,等待终止
通常,一个进程允许将其环境传播给子进程,但有时也有这种情况,进程想要给子进程指定一个确定的环境。(子承父业,包办婚姻)
新进程从调用进程继承了哪些属性?? p201
执行时关闭标志(close-on-exec):若在文件描述符上设置了此标志,那么在执行exec时,就会关闭该文件描述符。默认是没有设置的。
exec会关闭打开的目录流。
exec前后实际用户ID和实际组ID保持不变,而有效ID是否改变取决于所执行程序文件的设置用户ID位和设置组ID位是否设置。
基本认识:
- 当程序需要增加特权,或需要访问当前并不允许访问的资源时,需要更换用户ID和组ID。
- 当程序需要降低特权,或阻止对某些资源的访问时,也需要更换用户ID和组ID。
- 也就是说,我们在设计程序时,总是试图使用最小特权原则,安全。
setuid和setgid的规则好麻烦。P204,参考P206的实例分析。
以cmdline字符串的形式执行系统命令。
system实现中调用了fork,exec,waitpid。
系统调用被信号中断时有什么反应?
安全漏洞:
我们赋予tsys程序的超级用户权限在system中执行了fork和exec之后仍然被保留了下来。应当在fork之前,和exec之后,该回普通的权限。