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()创建出的轻量级进程。