进程是正在执行的程序,是资源分配的基本单位。
线程是进程内部的不同执行路径,是资源进行调度的基本单位,一个进程有多个线程,它们共享进程资源。
进程和线程都要各自的生命周期以及通信方式。
进程是资源分配的基本单位,但是线程不拥有资源,线程可以访问隶属于进程的资源。
线程是调度的基本单位,在同一进程中,线程的切换并不会引起进程的切换。一个进程的线程切换到不同进程的线程会引起进程的切换。
在创建,销毁以及切换进程时所带来的消耗远远大于线程的创建,销毁以及切换。
进程和线程的应用场景
进程是资源分配的基本单位,线程是资源调度的基本单位。
属于同一进程的线程,堆是共享的,栈是私有的。且所有的线程都具有连续的地址空间。
- 多进程的优点
内存隔离,可以尽量减少线程加锁/释放锁所带来的消耗
每个进程相对独立,子进程的奔溃并不会影响到主进程。 - 多进程缺点
进程间的调用,通讯,切换所带来的消耗均比多线程大。 - 多进程使用场景
如果目标子功能交互少,以及资源允许可以采用多进程方式。 - 多线程优点
无须跨越进程边界,并且多个线程可以直接共享进程内存与变量等,多线程所带来的消耗较小。 - 多线程缺点
并没有内存隔离,线程之间的同步和加锁比较麻烦。单个线程的奔溃很有可能影响到整个程序。
一般来说,频繁的任务创建,销毁并且切换以及大量计算任务优先使用多线程。
孤儿进程、僵尸进程与守护进程
孤儿进程就是说一个父进程退出,而它的一个或多个子进程还在运行,那么这些子进程将成为孤儿进程。孤儿进程将被 init 进程(进程ID为1的进程)所收养,并由 init 进程对它们完成状态收集工作。因为孤儿进程会被 init 进程收养,所以孤儿进程不会对系统造成危害。
僵尸进程就是一个子进程的进程描述符在子进程退出时不会释放,只有当父进程通过 wait() 或 waitpid() 获取了子进程信息后才会释放。如果子进程退出,而父进程并没有调用 wait() 或 waitpid(),那么子进程的进程描述符仍然保存在系统中,这种进程称之为僵尸进程。僵尸进程通过 ps 命令显示出来的状态为 Z。
系统所能使用的进程号是有限的,如果产生大量僵尸进程,可能会因为没有可用的进程号而导致系统不能产生新的进程。如果要消灭系统中大量的僵尸进程,只需要将其父进程杀死,此时僵尸进程就会变成孤儿进程,从而被 init 进程所收养,这样 init 进程就会释放所有的僵尸进程所占有的资源,从而结束僵尸进程。
守护进程是运行在后台的一种特殊进程,它是独立于控制终端的,并周期性地执行某些任务。
进程间同步方式
- 临界区
临界区是一段代码,在临界区内进程将访问临界资源。任何时候最多只有一个进程可以进入临界区。
优点:保证在某一时刻只有一个线程能访问数据的简便办法
缺点:虽然临界区同步速度很快,但却只能用来同步本进程内的线程,而不可用来同步多个进程中的线程。
- 互斥量
就是使用一个互斥的变量来直接制约多个进程,互斥对象只有一个,只有拥有互斥对象的线程才具有访问资源的权限。
优点:可以实现不同进程的线程对资源的安全共享。
缺点:比临界区带来更多资源消耗,只能控制一个线程访问该资源。
- 信号量
信号量相当于一个计数器,它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。互斥量是信号量的一种特殊情况,当信号量的最大资源数=1就是互斥量了。
缺点:信号量机制必须有公共内存,不能用于分布式操作系统,这是它最大的弱点。
- 事件机制
允许一个线程在处理完一个任务后,主动唤醒另外一个线程执行任务
线程间的同步方式
- synchronize
- volatile
- reentrantlock
- threadLocal:每个线程都创建一个资源的副本,互相独立互不影响
- automic 原子类
进程的内存布局
文本段:记录程序执行的指令等等
初始化数据段:包含了初始化的全局变量
未初始化数据段:没有包含初始化的全局变量
栈段
堆段