1. exec()
UNIX环境高级编程
当进程调用exec()函数时,该进程的执行程序完全替换成新程序,而新程序则从其main()函数开始执行。
调用exec()并不创建新进程,前后的进程ID并未改变,只是用磁盘上的新程序替换了当前程序的正文段、数据段和堆栈
2. exit()
UNIX环境高级编程
2.1 进程终止的5种正常方式
- main函数执行return语句,等效于调用
exit()
- 调用
exit()
函数。但是exit()
并不处理文件描述符、多进程(父进程和子进程)和作业控制 - 调用
_exit
或者_Exit
,目的是为进程提供一种无需运行终止处理程序的方法 - 进程最后一个线程执行return语句,但是该线程的返回值不用做进程的返回值。当最后一个线程return时,进程最后以终止状态0返回
- 进程最后一个线程调用
pthread_exit
函数
2.2 进程终止的3种异常方式
- 调用
abort
产生异常终止 - 当接受到进程自身(调用
abort
)、其他进程、或者内核产生 - 最后一个线程对"取消"请求作出响应。
3. fork()
UNIX环境高级编程
#include <unistd.h>
pid_t fork(void);
3.1 用途
由fork创建的进程被称为子进程。fork函数被调用一次,返回返回两次。
- 子进程的返回值为0
- 父进程的返回值为子进程的进程ID
父进程和子进程继续执行fork之后的指令,子进程是父进程的副本,子进程可以获取父进程的数据空间、堆栈的副本。
注意:是拥有副本,不是共享存储空间。他们只共享正文段
现在的技术是采用了写时复制的方法,即父进程和子进程先共享内存区域,但是内核会把内存区域设置为只读,如果需要写时,则内核只为写的区域制作一个副本,这个区域通常是虚拟存储页中的一页。
一般来说,fork之后的先执行父进程还是子进程都是不确定的,取决于内核使用的调度算法。
3.2 用法
- 父进程希望复制自己,使父进程和子进程同时执行不同的代码段。
网络服务中常见的请求:父进程等待客户端的服务请求。当这种请求到达时,父进程调用fork,使子进程处理此请求。父进程则继续等待下一个服务请求。
- 一个进程要执行一个不同的程序。
这对shell是常见的情况,子进程从fork返回后,立即调用exec。这个组合成为
spwan
4. vfork()
UNIX环境高级编程
vfork()
也用于创建一个新进程,该进程的目的就是执行一个新程序。
与fork()
的区别
- 该函数产生的子进程在执行
exec
或者exit
之前,它在父进程的空间中运行。不过此时子进程修改数据就会造成未知的后果。 - 这种创建方式比
fork
方式更快,毕竟不复制任何数据比写实复制策略的部分复制还是要快
5. clone()
clone()
是C语言库中定义的一个封装函数,负责建立新轻量级进程的堆栈。
传统的fork()
和vfork()
在Linux中是用clone()
实现的,此时此轻量级进程与父进程共享一块内存区域。当然这块内存区域是只读的,由于写时复制的存在,一旦父进程或者子进程想要修改此内存区域(修改堆栈中的值),则立即各自得到用户态堆栈的一根拷贝。
Linux使用轻量级进程对多线程程序提供更好的支持,两个轻量级进程可以共享一些资源(地址空间,打开的文件等),只要一个修改共享资源,另外一个就可以立即查看到这种修改。
因为Java本身就是多线程的程序,JVM一旦启动就有多个线程工作,例如垃圾收集线程和自己的业务线程。多线程的实现就是把这些轻量级进程和每个线程关联起来,这样线程之间的通信就是简单的通过共享一块内存地址空间、打开同一文件集。
同时,又因为此时的每个线程(轻量级进程)可以由内核单独调度,所以可以实现一个线程在sleep的时候,另外的线程可以运行。
6. 什么是Java线程
使用clone()
创建出的轻量级进程。