Java教程

并发和并行,协程,孤儿进程,僵尸进程,守护进程,进程组,会话

本文主要是介绍并发和并行,协程,孤儿进程,僵尸进程,守护进程,进程组,会话,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

文章目录

  • 并发和并行
  • 协程
    • 协程与线程的区别
  • 孤儿进程
    • 收养
  • 僵尸进程
  • 守护进程
  • 进程组
  • 会话

并发和并行

并发

在操作系统中,某一个时间段中,几个程序在同一个CPU上运行,但是在任意一个时间点上,只有一个程序在CPU上运行。

当有多个线程时,如果系统只有一个CPU,那么CPU不可能真正同时运行多个线程,CPU的运行时间会被划分成若干个时间段,每个时间段分配给各个线程去执行,一个时间段内某个线程运行时,其他线程处于挂起状态,这就是并发。

并行

当操作系统有多个CPU时,一个CPU处理A线程,另一个CPU处理B线程,两个线程互相不抢占CPU资源,可以同时进行,这种方式称为并行。

区别

  • 并发只是在宏观上给人感觉有多个程序在同时运行,但在实际的单CPU系统中,每一时刻只有一个程序在运行,微观上这些程序是分时交替执行的。
  • 在多CPU系统中,将这些并发执行的程序分配到不同的CPU上处理,每个CPU用来处理一个程序,这样多个程序便可以实现同时执行。

协程

协程是一种用户态的轻量级线程,协程的调度完全由用户控制。从技术的角度上来说,“协程就是你可以暂停执行的函数”。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,再切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则没有内核切换的开销。

协程与线程的区别

  • 一个线程可以拥有多个协程,一个进程也可以单独拥有多个协程。
  • 线程进程都是同步机制,而协程则是异步。
  • 协程能够保留上次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态。
  • 线程是抢占式的,而协程是非抢占式的,所以需要用户自己释放使用权来切换到其他协程。
  • 协程并不是取代线程,而是抽象于线程之上,线程是被分割的CPU资源,协程是组织好的代码流程,协程需要线程来承载运行,线程是协程的资源,但协程不会直接使用线程,协程直接利用的是执行器,执行器可以关联任意线程或线程池。

孤儿进程

孤儿进程指的是在其父进程执行完成或被终止后仍继续运行的一类进程。

收养

在类UNIX操作系统中,为避免孤儿进程退出时无法释放所占用的资源而僵死,任何孤儿进程产生时都会立即为系统进程init自动接受为子进程,这一过程也称为”收养“。在此需注意,虽然事实上该进程已有init作为其父进程,但由于创建该进程的进程已不存在,所以仍称为孤儿进程。

僵尸进程

在类UNIX系统中,僵尸进程是指完成执行(通过exit系统调用,或运行时发生致命错误或收到终止信号所致),但在操作系统的进程表中仍然存在其进程控制块,处于”终止状态“的进程这发生于子进程需要保留表项以允许其父进程读取子进程的退出状态:一旦退出态通过wait系统调用读取,僵尸进程条目就从进程表中删除,称之为”回收“。正常情况下,进程直接被其父进程wait并由系统回收。进程长时间保持僵尸状态一般是错误的并导致资源泄露。

与正常进程不同,kill命令对僵尸进程无效。孤儿进程不同于僵尸进程,其父进程已经死掉,但孤儿进程仍能正常执行,但并不会变为僵尸进程,因为被init收养并wait其退出。

子进程死后,系统会发送SIGCHLD信号给父进程,父进程对其默认处理是忽略。如果想响应这个消息,父进程通常在信号事件处理程序中,使用wait系统调用来响应子进程的终止。

僵尸进程被回收后,其进程号与在进程表中的表项都可以被系统重用。但如果父进程没有调用wait,僵尸进程将保留进程表中的表项,导致了资源泄露。某些情况下这反倒是期望的:父进程创建了另外一个子进程,并希望具有不同的进程号。如果父进程通过设置事件处理函数为SIG_IGN显式忽略SIGCHLD信号,而不是隐式默认忽略该信号,所有子进程的退出状态信息将被抛弃并且直接被系统回收

收割僵尸进程的方法是通过kill命令手工向其父进程发送SIGCHLD信号。如果其父进程仍然拒绝回收僵尸进程,则终止父进程,使得init进程收养僵尸进程。init进程周期执行wait系统调用回收其收养的所有僵尸进程。

为避免产生僵尸进程,实际应用中一般采取的方式是

  1. 将父进程中对SIGCHLD信号的处理函数设置为SIG_IGN(忽略信号)
  2. fork两次并杀死一级子进程,令二级子进程称为孤儿进程而被init收养,清理

守护进程

守护进程是生存长的一种进程。它们常常在系统引导装入时启动,仅在系统关闭时才终止。因为它们没有控制终端,所以说它们是在后台运行的。UNIX系统有很多守护进程,它们执行日常事务活动。

大多数守护进程都以超级用户root特权运行。所有的守护进程都没有控制终端,其终端名设置为问号。内核守护进程以无控制终端方式启动。用户层守护进程缺少控制终端可能是守护进程调用了setsid的结构。大多数用户层守护进程都是进程组的组长进程以及会话的受进程,而且是这些进程组和会话的唯一进程。

应当引起注意的是用户层守护进程的父进程是init进程

进程组

每个进程除了有一个进程ID之外,还属于一个进程组。

进程组是一个或多个进程的集合。通常,它们是在同一作业中结合起来的,同一进程组中的各进程接收来自同一终端的各种信号。每个进程组有一个唯一的进程组ID。进程组ID类似于进程ID——它是一个正整数,并可存放在pid_t数据类型中。

函数getpgrp返回调用进程的进程组ID

#include <unistd.h>
pid_t getpgrp(void);
//返回值:调用进程的进程组ID

每个进程组有一个组长进程。组长进程的进程组ID等于其进程ID

进程组组长可以创建一个进程组,创建该组中的进程,然后终止。只要在某个进程组中有一个进程存在,则该进程组就存在,这与其组长是否终止无关。从进程组创建开始到其中最后一个进程离开为止的事件区间称为进程组的生命期。某个进程组中的最后一个进程可以终止,也可以转移到另一个进程组。

一个进程只能为它自己或它的子进程设置进程组ID。在它的子进程调用exec后,他就不能再更改子进程的进程组ID。

会话

会话是一个或多个进程组的集合

进程调用setsid函数建立一个新会话。

#include <unistd.h>
pid_t setsid(void);
//返回值:若成功,返回进程组ID;若出错,返回-1

如果调用此函数的进程不是一个进程组的组长,则此函数创建一个新会话。具体会发生以下3件事。

  1. 该进程变成新会话的会话首进程(会话首进程是创建该会话的进程)。此时,该进程是新会话中的唯一进程。
  2. 该进程成为一个新进程组的组长进程。新进程组ID是该调用进程的进程ID。
  3. 该进程没有控制终端。如果在调用setsid之前该进程有一个控制终端。如果在调用setsid之前该进程有一个控制终端,那么这种联系被切断。

如果该调用进程已经是一个进程组的组长,则此函数返回出错。为了保证不处于这种情况,通常先调用fork,然后使其父进程终止,而子进程则继续。因为子进程继承了父进程的进程组ID,而其进程ID则是新分配的,两者不可能相等,这就保证了子进程不是一个进程组的组长。

这篇关于并发和并行,协程,孤儿进程,僵尸进程,守护进程,进程组,会话的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!