Linux常见进程类别

目录

常见进程类别

守护进程&精灵进程

任务管理

进程组

作业

作业 | 进程组

会话

w命令

守护进程

守护进程的创建

setsid()函数

daemon()函数

模拟实现daemon函数

前台进程 | 后台进程

僵尸进程 | 孤儿进程

僵尸进程的一些细节

守护进程 | 后台进程

守护进程 | 僵尸进程 | 孤儿进程


常见进程类别

  • 僵尸进程:子进程先于父进程退出,父进程没有对子进程的退出进行处理,因此子进程会保存自己的退出信息而无法释放所有资源成为僵尸进程导致资源泄露。
  • 孤儿进程:父进程先于子进程退出,子进程成为孤儿进程,运行在后台,父进程成为1号进程(而孤儿进程的退出,会被1号进程负责任的进行处理,因此不会成为僵尸进程)
  • 守护进程&精灵进程:这两种是同一种进程的不同翻译,是特殊的孤儿进程,不但运行在后台,最主要的是脱离了与终端和登录会话的所有联系,也就是默默的运行在后台不想受到任何影响。
  • 前台进程:前台的任务一启动,键盘输入的命令就是没有任何效果的,并且可以被CTRL c终止的进程。

  • 后台进程:我们可以通过加一个&让前台进程变为在后台进行。当让其在后台进行时,命令是可以正常执行的,CTRL c是没有办法终止其的。

#:前台进程和后台进程在Linux系统中有着不同的意义和作用。

  • 前台进程:是指当前正在运的程序所在的终端窗口处于活动状态,并且该程序正在终端窗口中输出信息或等待用户输入。此时,用户无法在该终端窗口中输入其他命令,直到该程序运行完毕或被用户手动停止。因此,前台进程通常用于需要用户交互的应用程序,例如编辑器、终端等。
  • 后台进程:是指在后台运行的程序,它们不会占用当前终端窗口并且不会阻塞用户输入。用户可以在终端中同时执行多个命令,并且可以将一个正在运行的前台进程转为后台进程,使得该进程在后台继续执行而不影响用户操作。因此,后台进程通常用于需要长时间运行的任务或者需要在系统启动时自动运行的服务程序。
  • ctr+z 将当前运行的程序放入后台挂起。
  • jobs 命令,显示后台被挂起的所有进程。
  • bg N 使第N个序号的任务在后台(background)运行。
  • fg N 使第N个序号的任务在前台(foreground)运行。

守护进程&精灵进程

        精灵进程&守护进程是一样的,不同的翻译叫法而已。

任务管理

进程组

        在Linux系统中,每个进程都属于一个进程组。进程组是一组相关联的进程的集合,这些进程通常是由同一个父进程创建的,并且它们之间可以相互协作。每个进程组都有一个唯一的进程组ID(PGID),而每个进程也有一个唯一的进程ID(PID)。在同一个进程组中,其中一个进程会被指定为该组的领头进程(也称为“组长”),其PID等于PGID。只要在某个进程组中有一个进程存在,则该进程组就存在,这与其组长进程是否终止无关。

#:进程必定属于一个进程组,也只能属于一个进程组。

        在Unix/Linux系统中,可以使用setpgid()系统调用将一个子进程添加到不同的进程组中。使用getpgrp()系统调用来获取当前进程所在进程组号。

#include <unistd.h>
int setpgid(pid_t pid, pid_t pgid);
  • 返回值:
    • 执行成功则返回组识别码,如果有错误则返回-1,错误原因存于errno中。
  • 参数:
    • pid参数指定要设置的进程ID。
      • 如果参数pid为0,则会用来设置目前进程的组识别码。
    • pgid参数指定要设置的进程组ID。
      • 如果参数pgid为0,则会以目前进程的进程识别码来取代。
    • 如果pid和pgid相等,则该进程将成为一个新进程组的组长。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>int main()
{pid_t id = fork();if (id == 0) {// 子进程printf("Child process: PID=%d, PGID=%d\n", getpid(), getpgrp());// 将子进程添加到新的进程组中setpgid(0, 0);printf("Child process after setpgid(): PID=%d, PGID=%d\n", getpid(), getpgrp());// 子进程执行一些任务sleep(10);exit(EXIT_SUCCESS);}else if(id > 0){// 父进程printf("Parent process: PID=%d, PGID=%d\n", getpid(), getpgrp());// 父进程执行一些任务sleep(5);exit(EXIT_SUCCESS);}else{perror("fork");exit(EXIT_FAILURE);}
}

        子进程首先输出自己的PID和PGID,然后使用setpgid()函数将自己添加到一个新的进程组中,并再次输出自己的PID和PGID。父进程也输出自己的PID和PGID,并在子进程执行任务期间休眠5秒钟。运行该程序后,就可以看到子进程的PGID与父进程不同,即子进程已经成为一个新的进程组的组长。 

[qcr@VM-16-6-centos test_2023_9_9]$ ./test 
Parent process: PID=10606, PGID=10606
Child process: PID=10607, PGID=10606
Child process after setpgid(): PID=10607, PGID=10607

作业

        作业指的是在终端或者控制台上运行的进程。当你在命令行输入一个命令并按下回车键后,这个命令就会以进程的方式在后台运行,并且会被分配一个唯一的作业号(job ID)。你可以使用job ID来管理和控制这个进程。在Linux中,有两种类型的作业:前台作业和后台作业。前台作业是指当前正在终端或者控制台上运行的进程,而后台作业则是指在后台运行的进程。

作业控制控制前后台进程的步骤:

1、启动一个进程并将其放到后台运行
        在命令行终端上启动一个命令时,在该命令末尾加上 '&' 符号即可将其放到后台运行。例如:

$ long_running_command &

2、查看当前正在运行的作业
        可以使用 'jobs' 命令查看当前正在运行的作业及其状态。例如:

$ jobs
[1]+  Running                 long_running_command &

3、将后台进程调回前台
        可以使用 'fg' 命令将一个后台进程调回前台。例如:

$ fg n

4、将前台进程放到后台
        可以使用 'Ctrl+Z' 键将当前前台进程暂停,并将其放到后台运行,但使用Ctrl+Z后该进程就会处于停止状态(Stopped)。
例如:

^Z
[1]+  Stopped                 long_running_command

5、恢复被暂停的后台进程
        可以使用 'bg' 命令将被暂停的后台进程恢复运行。例如:

$ bg n
[1]+ long_running_command &

6、终止进程
        可以使用 'Ctrl+C' 键向前台进程发送 'SIGINT' 信号,终止该进程的运行。如果想要强制终止一个进程,可以使用 'kill' 命令。例如:

$ kill -9 <pid>

作业 | 进程组

  • 进程组:是一个进程 / 具有相同进程组ID的一组进程,它们之间没有必然的关联,但是可以通过进程组ID方便地对它们进行管理。

#:融会贯通的理解

        假设要完成一个任务,需要同时并发100个进程。当用户处于某种原因要终止这个任务时,要是没有进程组,就需要手动的一个个去杀死这100个进程,并且必须要严格按照进程间父子兄弟关系顺序,否则会扰乱进程树。有了进程组,就可以将这100个进程设置为一个进程组,它们共有1个组号(pgrp),并且有选取一个进程作为组长(通常是“辈分”最高的那个,通常该进程的ID也就作为进程组的ID)。现在就可以通过杀死整个进程组,来关闭这100个进程,并且是严格有序的。组长进程可以创建一个进程组,创建该组中的进程,然后终止。(来源:Linux-进程、进程组、作业、会话、控制终端详解 - John_ABC

  • 作业:是一个进程 / 具有关联联系的多个进程,这些进程通常由同一个父进程创建,并且它们共享同一个终端。

#:融会贯通的理解

        在Linux中,一个进程可以通过管道(pipe)、作业控制(job control)、信号(signal)等方式与其他进程进行直接关联。

        因此,尽管作业和进程组都是由多个程序或任务组成的集合,但它们之间有所不同。作业更加高级,包含更多元素(如文件、输入输出等),而进程组则更注重管理和控制多个相关联的进程。

#:都是操作系统中的概念,用于方便对一组相关联的进程进行管理和控制。

        作业与进程组的区别:如果作业中的某个进程又创建了子进程,则子进程不属于作业。一旦作业运行结束,Shell就把自己提到前台,如果原来的前台进程还存在(如果这个子进程还没终止),它自动变为后台进程组。父进程创建的子进程默认情况下属于同一进程组。在Unix/Linux系统中,每个进程都有一个唯一的进程ID(PID)和一个进程组ID(PGID)。当父进程创建子进程时,子进程会继承父进程的PGID,因此它们属于同一进程组。

会话

        由于Linux是多用户多任务的分时系统,所以必须要支持多个用户同时使用一个操作系统。当一个用户登录一次系统就形成一次会话 。一个会话可包含多个进程组,但只能有一个前台进程组。每个会话都有一个会话首领(leader),即创建会话的进程。

        一个用户可以在同一时间拥有多个会话。在Linux系统中,每个用户都可以通过终端、SSH等方式登录到系统中,并启动一个新的会话。这些会话可以同时运行不同的进程和任务,且互相独立,互不干扰。每个会话都有一个唯一的ID号,称为Session ID(SID),用于区分不同的会话。而每个进程也有一个唯一的Process ID(PID),用于区分不同的进程。通过命令 "who" 或者 "w" 可以查看当前登录系统的用户和他们所拥有的会话。

w命令

        w命令实际上用于显示当前终端的前台进程,而一个会话只能有一个前台进程。因此,通过观察w命令的输出,所以可以间接推断出当前系统中有多少个会话。

(此处有一个会话执行w命令,所以w对应的进程变为了前台进程,bash变为了后台进程)

w命令的属性字段:

USER   : 登录用户的用户名。
TTY    : 登录用户所使用的终端设备。
FROM   : 登录用户的IP地址或远程主机名。
LOGIN@ : 登录时间,格式为月日时分。
IDLE   : 用户空闲时间,即从上次输入开始到现在的时间。
JCPU   : 所有进程占用CPU时间的总和,单位为分钟。
PCPU   : 当前进程占用CPU时间百分比。
WHAT   : 当前登录用户所执行的命令或进程。

参数说明

-f  开启或关闭显示用户从何处登入系统。
-h  不显示各栏位的标题信息列。
-l  使用详细格式列表,此为预设值。
-s  使用简洁格式列表,不显示用户登入时间,终端机阶段作业和程序所耗费的CPU时间。
-u  忽略执行程序的名称,以及该程序耗费CPU时间的信息。
-V  显示版本信息。

#:融会贯通的理解

        一个会话可以有一个控制终端。这通常是登陆到其上的终端设备(在终端登陆情况下)或伪终端设备(在网络登陆情况下)。建立与控制终端连接的会话首进程被称为控制进程。一个会话中的几个进程组可被分为一个前台进程组以及一个或多个后台进程组。所以一个会话中,应该包括控制进程(会话首进程),一个前台进程组和任意后台进程组。 


        在Linux/Unix系统中,当我们启动一个新的shell时,通常情况下会先由init进程启动一个getty或login进程,当我们在终端输入用户名和密码后,getty或login进程会验证用户身份,并为该用户创建一个新的会话(session)和控制终端(controlling terminal)。然后,该进程会使用exec函数族调用启动一个新的shell程序(如bash),并将其加入到该会话的前台进程组中。此时,init进程成为了该会话的控制进程,而bash进程则成为了该会话的前台进程#:前台进程只能有一个,当一个进程变成前台进程后,bash会自动变为后台进程,此时bash就无法进行命令行解释了。

        setsid()调用能创建一个会话。必须注意的是,只有当前进程不是进程组的组长时,才能创建一个新的会话。调用setsid 之后,该进程成为新会话的leader。

#include <unistd.h>pid_t setsid(void);

        使用 'setsid' 函数可以将一个进程从其父进程所在的终端分离出来,使其成为一个独立的后台进程,并且不再受到终端关闭等事件的影响。

  1. 创建一个子进程(fork)。
  2. 在子进程中调用 'setsid' 函数创建一个新会话。
  3. 关闭子进程中不需要的文件描述符(close)。
  4. 更改当前工作目录(chdir)。
  5. 重定向标准输入、输出、错误输出到/dev/null或其他文件(dup2)。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main()
{pid_t pid;// 创建子进程pid = fork();if(pid == -1){perror("fork");exit(EXIT_FAILURE);}else if(pid > 0){// 父进程退出exit(EXIT_SUCCESS);}// 在子进程中创建新会话if(setsid() == -1){perror("setsid");exit(EXIT_FAILURE);}// 关闭不需要的文件描述符close(STDIN_FILENO);close(STDOUT_FILENO);close(STDERR_FILENO);// 更改当前工作目录chdir("/");// 重定向标准输入、输出、错误输出到/dev/nullint fd = open("/dev/null", O_RDWR, 0);dup2(fd, STDIN_FILENO);dup2(fd, STDOUT_FILENO);dup2(fd, STDERR_FILENO);while(1){// 后台进程逻辑代码sleep(1);}return 0;
}

        该程序在执行时,会创建一个后台进程,并将其从终端分离出来。后台进程的逻辑代码可以在while循环中实现,此处只是简单地使用sleep函数模拟了一下。

守护进程

#:守护进程也称精灵进程(Daemon),是运行在后台的一种特殊进程(一种长期运行的后台进程),它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件,如监控硬件设备、网络服务等。

特点:

  1. 通常是由系统管理员启动的,而不是由用户手动启动。
  2. 通常不与终端关联,因此没有标准输入、输出和错误输出。
  3. 通常需要以root权限运行,以便访问系统资源。
  4. 通常会定期检查某些事件或条件,并根据需要采取相应的操作。
  5. 通常会将自己作为后台进程运行,并将自己从父进程中分离出来。
  6. 通常会将文件描述符重定向到/dev/null或其他日志文件中,以避免产生不必要的输出信息。

        凡是TPGID一栏写着-1的都是没有控制终端的进程,也就是守护进程。

        在COMMAND一列用 [ ] 括起来的名字表示内核线程,这些线程在内核里创建,没有用户空间代码,因此没有程序文件名和命令行,通常采用以k开头的名字,表示Kernel

守护进程的创建

setsid()函数

        setsid()调用能创建一个会话。必须注意的是,只有当前进程不是进程组的组长时,才能创建一个新的会话。调用setsid 之后,该进程成为新会话的leader。

#include <unistd.h>pid_t setsid(void);
  • 返回值
    • 如果调用成功,返回新的会话ID。
    • 如果调用失败,返回 -1 并设置errno变量。
  1. 在父进程中调用fork(),然后使父进程exit(),这样可以让子进程继承父进程的会话(session)和进程组(process group)。
  2. 在子进程中调用setsid()函数,建立一个新的会话(session),使子进程成为该新会话的领头进程(leader process),同时也成为一个新的进程组(group)的领头进程,从而与原来的控制终端分离。如果setsid返回-1,则表示出错。
  3. 为了防止在某个时刻重新获得控制终端,需要再次fork()一次,并使父进程exit()。这样保证了守护进程不会重新获得控制终端。
  4. 在守护进程中修改工作目录(chdir())和文件屏蔽字(umask())等环境变量,以便更好地适应后台运行环境。
  5. 守护进程不能直接和用户交互,也就是说守护进程已经与终端去关联了,因此一般我们会将守护进程的标准输入、标准输出以及标准错误都重定向到/dev/null/dev/null是一个字符文件(设备),通常用于屏蔽/丢弃输入输出信息。
  6. 正式执行守护进程任务代码。
  7. 在程序结束时释放资源并退出。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>int main()
{// 创建子进程pid_t pid = fork();// 如果创建子进程失败,则直接退出if(pid == -1){perror("fork");exit(1);}// 如果是父进程,则直接退出if(pid > 0){exit(0);}// 在子进程中创建新会话,并成为领头进程if(setsid() == -1){perror("setsid");exit(1);}// 第二次fork,创建孙子进程pid = fork();// 如果创建孙子进程失败,则直接退出if(pid == -1){perror("fork");exit(1);}// 如果是父进程,则直接退出if(pid > 0){exit(0);}// 将标准输入、标准输出、标准错误重定向到/dev/nullclose(0);int fd = open("/dev/null", O_RDWR);dup2(fd, 1);dup2(fd, 2);// 修改工作目录和文件屏蔽字chdir("/");umask(0);// 正式执行守护进程任务代码while(1){// do something...sleep(10);}return 0;
}

        第二次fork()主要是为了避免守护进程在未来可能会因为某些原因重新获得控制终端而导致进程退出的问题。

#:融汇贯通的理解

        具体来说,会有个SIGHUP信号,其是一种由操作系统发送给进程的信号,通常表示 “终端挂起” 。在Linux操作系统中,当一个进程打开了一个终端设备(例如控制台窗口),并且该终端设备被关闭或者与该进程失去联系时,操作系统会向该进程发送SIGHUP信号。
        在守护进程中,如果只进行一次fork()操作,则该子进程仍然会与原来的控制终端相关联。如果此时用户关闭了控制终端,则操作系统会向该子进程发送SIGHUP信号。如果程序没有处理这个信号,则可能会导致程序异常退出。
        因此,需要进行第二次fork()操作。这样,在第二次fork()之后,会产生一个孙子进程,并使孙子进程成为真正的守护进程。而孙子进程与其父进程和原来的控制终端、信号等都已经完全脱离关系。

[qcr@VM-16-6-centos test_2023_9_10]$ ps axj | head -1 && ps axj | grep testPPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND1  4382  4381  4381 ?           -1 S     1001   0:00 ./test9217  4483  4482  9160 pts/0     4482 S+    1001   0:00 grep --color=auto test

        运行代码,用ps命令查看该进程,会发现该进程的TPGID为-1,TTY显示的是 也就意味着该进程已经与终端去关联了。

        进程的SID与bash进程的SID是不同的,即它们不属于同一个会话。

  • 通过ls /proc/进程id -al命令,可以看到该进程的工作目录已经成功改为了根目录。
  • 通过ls /proc/进程id/fd -al命令,可以看到该进程的标准输入、标准输出以及标准错误也成功重定向到了/dev/null

/proc

        在Linux系统中,/proc是一个虚拟文件系统,它提供了对系统内核和进程的信息的访问。/proc目录下包含了很多文件和子目录,每个文件或子目录都代表着一个进程或系统内核的一些信息。

/proc/cpuinfo  :  包含了CPU的信息。
/proc/meminfo  :  包含了内存的信息。
/proc/net      :  包含了网络相关的信息。
/proc/sys      :  包含了一些内核参数和系统配置信息。
/proc/<pid>    :  代表着一个进程,其中pid是进程的ID号。该目录下包含了该进程的很多信息,如进程状态、打开的文件、内存映射、线程等。

daemon()函数

        在Unix/Linux系统中,我们可以使用daemon()函数来创建Daemon进程。

#include <unistd.h>
int daemon(int nochdir, int noclose);
  • 参数
    • nochdir:如果该参数为0,则在调用daemon()函数后,进程的当前工作目录会被设置为根目录 "/" ;如果该参数不为0,则进程的当前工作目录不会改变。
    • noclose:如果该参数为0,则在调用daemon()函数后,进标准输入、标准输出以及标准错误重定向到/dev/null;如果该参数不为0,则进程不会关闭任何文件描述符。
  • 返回值
    • 0表示成功。
    • 返回-1表示失败,并设置errno变量。
#include <unistd.h>int main()
{daemon(0, 0);while (1);return 0;
}

        调用daemon函数创建的守护进程与setsit函数创建的守护进程差距不大,唯一区别就是daemon函数创建出来的守护进程,既是组长进程也是会话首进程。也就是说系统实现的daemon函数没有防止守护进程打开终端,因此我们实现的反而比系统更加完善。

模拟实现daemon函数

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>int my_daemon(int nochdir, int noclose)
{pid_t pid;// 创建子进程pid = fork();if (pid == -1) {perror("fork error");exit(EXIT_FAILURE);} else if (pid > 0) {// 父进程退出exit(EXIT_SUCCESS);}// 子进程调用setsid()函数创建新会话,并成为会话组长和进程组长if (setsid() == -1) {perror("setsid error");exit(EXIT_FAILURE);}// 忽略SIGHUP信号signal(SIGHUP, SIG_IGN);// 再次创建子进程,退出父进程,保证守护进程不是会话组长和进程组长pid = fork();if (pid == -1) {perror("fork error");exit(EXIT_FAILURE);} else if (pid > 0) {// 父进程退出exit(EXIT_SUCCESS);}// 如果nochdir参数为0,则将当前工作目录更改为根目录if (nochdir == 0) {chdir("/");}// 将标准输入、标准输出、标准错误重定向到/dev/nullif (noclose == 0){close(0);int fd = open("/dev/null", O_RDWR);dup2(fd, 1);dup2(fd, 2);}// 设置umask为0,以便守护进程可以创建任意权限的文件umask(0);return 0;
}

        my_daemon()函数首先通过调用 'fork()' 函数创建了一个子进程。然后,在父进程中直接退出,而在子进程中调用 'setsid()' 函数创建新会话,并成为会话组长和进程组长。接着,忽略SIGHUP信号,并再次创建子进程,退出父进程,保证守护进程不是会话组长和进程组长。最后,根据传入的参数设置工作目录、关闭标准输入输出和标准错误输出、以及设置umask。

前台进程 | 后台进程

#:前台进程后台进程是两个不同的概念!

  1. 前台进程是指正在与用户交互的进程,通常会占用屏幕显示资源,需要用户进行操作才能继续运行。而后台进程则是在后台默默运行的进程,用户通常无法直接看到或者进行交互。
  2. 前台进程通常是用户启动的应用程序或者系统服务,如浏览器、文本编辑器等,而后台进程通常是由系统自动启动或者其他程序调用的服务程序。
  3. 前台进程通常需要占用大量资源才能正常运行,如CPU、内存等;而后台进程则通常不需要太多资源,但是需要长时间稳定地运行。
  4. 前台进程,用户可以通过界面操作来控制其运行状态;而后台进程,则需要通过命令行工具或者其他管理工具来进行控制和管理。 

僵尸进程 | 孤儿进程

#:孤儿进程僵尸进程是两个不同的概念!

  1. 孤儿进程是指其父进程已经退出或者异常终止,但是该进程仍然在运行的进程。这种情况通常发生在父进程意外退出或者没有正确处理子进程退出状态的情况下。而僵尸进程则是指已经完成执行任务,但是其父进程还没有来得及处理它们的退出状态信息的进程。
  2. 孤儿进程会被init进程(PID为1)接管,并成为init的子进程,由init负责回收它们的资源。而僵尸进程则需要被及时清理以释放系统资源。
  3. 孤儿进程占用系统资源,如果大量存在则可能导致系统性能下降。而僵尸进程不会占用太多系统资源,但是如果存在过多则可能导致系统性能下降。
  4. 孤儿进程需要被接管并继续运行或者被正确处理,而僵尸进程则需要被及时清理以释放系统资源
  5. 孤儿进程是一个正在运行的进程,僵尸进程是一个已死的进程。

僵尸进程的一些细节

        僵尸进程已经死亡,因此无法使用kill命令杀死它们。实际上,僵尸进程的退出状态信息已经被内核保存了下来,但是其父进程没有及时处理这些信息,导致僵尸进程一直处于“僵死”状态。要彻底清除僵尸进程,需要通过其父进程将其退出状态信息处理完毕,并调用wait或waitpid函数来回收其资源。

#:僵尸进程父进程又没有回收?

父进程正执行:

        如果僵尸进程的父进程一直没有回收它,那么这个僵尸进程所占用的资源(如内存、文件描述符等)将一直被占用,直到系统重启或者其他操作将其释放。长时间存在大量僵尸进程会浪费系统资源,降低系统性能。

解决方案:

        可以通过手动杀死父进程来强制回收所有僵尸进程的资源(正是下一种方法)。但是这种操作需要谨慎处理,因为杀死父进程可能会影响其他正在运行的进程。更好的方法是通过修改程序代码,在父进程中添加对子进程退出状态信息的处理代码,及时回收子进程的资源。

父进程已死亡:

#:并不是网上有些说的会变为孤儿进程。

        此时,子进程的父进程ID会被设置为1,也就是init进程的ID。然后init进程会定期轮询检查是否有僵尸进程需要处理,并回收它们的资源。

        因此,终止父进程后僵尸进程并不会转变为孤儿进程交给init处理,而是由init负责回收其资源。

守护进程 | 后台进程

#:后台进程守护进程是两个不同的概念!可以看作守护进程是特殊的后台进程。

        后台进程是指运行在后台的进程,它们不会占用当前终端窗口并且不需要用户输入,而是在后台默默地执行。用户可以使用特定的命令将一个前台进程转化为后台进程,或者直接启动一个后台进程。而守护进程则是一种特殊的后台进程,它通常是由系统启动时自动启动的,并且会一直运行,直到系统关闭。它们通常不受用户交互影响,并且会在后台执行某些系统任务或服务。例如,网络服务(如Web服务器、FTP服务器等)就是通过守护进程来实现的。


后台进程相比守护进程特点:

  1. 守护进程通常会从父进程中脱离出来,成为一个独立的进程组和会话,并且会重新设置文件权限等环境变量。
  2. 守护进程通常会定期检查和处理一些任务,以保证其正常运行。例如,Web服务器会定期清理日志文件、更新缓存等操作。
  3. 守护进程通常会记录日志信息,并将其写入到指定的日志文件中,以便于管理员进行故障排查和问题定位。

        总之,后台进程和守护进程都是Linux系统中常见的进程类型,但守护进程通常会比普通的后台进程更加复杂和功能强大。 

守护进程 | 僵尸进程 | 孤儿进程

        精灵进程&守护进程是一样的,不同的翻译叫法而已,它的父进程是1号进程,退出后不会成为僵尸进程、孤儿进程。

#:init进程

        init进程是Linux系统中的第一个进程,其进程号始终为1。在Linux系统启动时,内核会首先启动init进程,并由它来启动其他所有进程。init进程主要负责初始化系统环境、启动各种服务和守护进程,并监控这些进程的运行状态。如果某个进程异常退出或崩溃,init进程会尝试重新启动该进程,以确保系统稳定运行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/75510.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++-map和set

本期我们来学习map和set 目录 关联式容器 键值对 pair 树形结构的关联式容器 set multiset map multimap 关联式容器 我们已经接触过 STL 中的部分容器&#xff0c;比如&#xff1a; vector 、 list 、 deque 、forward_list(C11)等&#xff0c;这些容器统称为序列式…

关于软件的功能复用

有一些人总在说软件要复用&#xff0c;开发一个项目时要想想怎么在另一个项目中能重用。你问他怎么做到复用&#xff0c;就会听到微服务、中台一些名词 复用的层次 说到复用&#xff0c;首先要想明白复用的是啥 级别越低&#xff0c;粒度越小&#xff0c;复用的范围越广&#…

Yolov5改进算法之添加Res2Net模块

目录 1. Res2Net介绍 1.1 Res2Net的背景和动机 1.2 Res2Net的基本概念 2. YOLOV5添加Res2Net模块 Res2Net&#xff08;Residual Resolution Network&#xff09;是一种用于图像处理和计算机视觉任务的深度卷积神经网络架构。它旨在解决传统的ResNet&#xff08;Residual Ne…

【java】【SSM框架系列】【三】Maven进阶

目录 一、分模块开发与设计 1.1 分模块开发的意义 1.2 分模块开发&#xff08;模块拆分&#xff09; 二、依赖管理 2.1 依赖传递 2.2 可选依赖 2.3 排除依赖 三、聚合与继承 3.1 聚合 3.2 继承 3.3 聚合与继承的区别 四、属性管理 4.1 属性 4.1.1 属性配置与使用 …

2023 年高教社杯全国大学生数学建模竞赛题目 B 题 多波束测线问题

B 题 多波束测线问题 单波束测深是利用声波在水中的传播特性来测量水体深度的技术。声波在均匀介质中作匀速直线传播&#xff0c;在不同界面上产生反射&#xff0c;利用这一原理&#xff0c;从测量船换能器垂直向海底发射声波信号&#xff0c;并记录从声波发射到信号接收的传播…

智慧工地:让工地可视化、数字化、智能化

智慧工地平台功能包括&#xff1a;劳务管理、施工安全管理、视频监控管理、机械安全管理、危大工程监管、现场物料监管、绿色文明施工、安全隐患排查、施工综合管理、施工质量管理、设备管理、系统管理等模块。 一、项目开发环境 技术架构&#xff1a;微服务 开发语言&#…

数据结构--- 树

(一)知识补充 定义 树是一种数据结构,它是由n(n≥0)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。​ 它具有以下的特点: 每个节点有零个或多个子节点; 没有父节点的节点称为根节点;每一个非根…

算法训练营day44|动态规划 part06:完全背包 (完全背包、 LeetCode518. 零钱兑换 II、377. 组合总和 Ⅳ )

文章目录 完全背包518. 零钱兑换 II (求组合方法数)思路分析代码实现思考总结 377. 组合总和 Ⅳ (求排列方法数)思路分析代码实现思考总结 完全背包 完全背包和01背包问题唯一不同的地方就是&#xff0c;每种物品有无限件。 依然举这个例子&#xff1a; 背包最大重量为4。 物…

【LInux编译器gcc/g++】gcc使用方法和动静态库相关概念

目录 一.前言 二.源代码的翻译环境 三.gcc相关指令 四.动静态库 1.什么是库&#xff1f; 2.库的命名 3.库的链接方式 4.动静态链接的优缺点 5.小结 一.前言 在Windows系统上我们常用VisualStudio来进行C/C开发&#xff0c;VS并不是一款单一的软件&#xff0c;而是集成…

【刷题篇】贪心算法(一)

文章目录 分割平衡字符串买卖股票的最佳时机Ⅱ跳跃游戏钱币找零 分割平衡字符串 class Solution { public:int balancedStringSplit(string s) {int lens.size();int cnt0;int balance0;for(int i0;i<len;i){if(s[i]R){balance--;}else{balance;}if(balance0){cnt;}}return …

WPF CommunityToolkit.Mvvm Messenger通讯

文章目录 环境WeakReferenceMessenger方法介绍无回调订阅发送Token区分有回调订阅发送 环境 CommunityToolkit.Mvvm Messenger 十月的寒流: 如何使用 CommunityToolkit.Mvvm 中的 Messenger 来进行 ViewModel 之间的通信 WeakReferenceMessenger 我这里只讲简单的弱Messenger…

Spring云服务:如何将应用程序轻松迁移到云端

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

Android图形-Hardware Composer HAL

目录 一、引言 二、概览 三、实现HWC 3.1 为什么是HWC&#xff1f; 3.2 HWC的支持需求 3.3 HWC的实现思路 3.4 HWC的基元 3.5 HIDL接口 3.6 函数指针 3.7 图层和屏幕句柄 3.8 屏幕合成操作 3.9 多个屏幕 3.10 虚拟屏幕合成 3.10.1 模式 3.10.2 输出格式 3.11 同…

Qt 5.15编译(MinGW)及集成Crypto++ 8.8.0笔记

一、背景 为使用AES加密库&#xff08;AES/CBC加解密&#xff09;&#xff0c;选用Crypto 库&#xff08;官网&#xff09;。   最新Crypto C库依次为&#xff1a;8.8.0版本&#xff08;2023-6-25&#xff09;、8.7.0&#xff08;2022-8-7&#xff09;和8.6.0&#xff08;202…

如何利用 Selenium 对已打开的浏览器进行爬虫

大家好&#xff01; 在对某些网站进行爬虫时&#xff0c;如果该网站做了限制&#xff0c;必须完成登录才能展示数据&#xff0c;而且只能通过短信验证码才能登录 这时候&#xff0c;我们可以通过一个已经开启的浏览器完成登录&#xff0c;然后利用程序继续操作这个浏览器&…

【LeetCode-中等题】39. 组合总和

文章目录 题目方法一&#xff1a;递归回溯 题目 这题的nums数组里面不存在重复元素&#xff0c;所以也就无需做去重操作 但同一个元素可以被无限次取&#xff0c;说明每次递归中的for循环的开始位置就是自己 nums数组里面存在重复元素&#xff0c;去重版本&#xff1a; 方法一…

Git学习记录

Contest 一、工作区域二、操作命令2.1 创建仓库2.2 查看仓库状态2.3 从工作区向暂存区添加文件2.3.1 只添加一个文件2.3.2 添加全部文件 2.4 从暂存区向仓库区添加文件2.5 查询日志2.5.1 从当前版本开始查询2.5.2 查看所有日志 2.6 回滚2.6.1 从仓库回滚到工作区2.6.2 取消工作…

剑指 Offer 04. 二维数组中的查找

题目描述 在一个 n * m 的二维数组中&#xff0c;每一行都按照从左到右 非递减 的顺序排序&#xff0c;每一列都按照从上到下 非递减 的顺序排序。请完成一个高效的函数&#xff0c;输入这样的一个二维数组和一个整数&#xff0c;判断数组中是否含有该整数。 解题思路 注意每…

c++中的list容器讲解

文章目录 1. list的介绍及使用1.1 list的介绍1.2 list的使用1.2.1 list的构造1.2.2 list iterator的使用1.2.3 list capacity1.2.4 list element access1.2.6 list的迭代器失效 2. list的模拟实现2.1 模拟实现list 3. list与vector的对比 1. list的介绍及使用 1.1 list的介绍 …

医疗知识图谱 neo4j

开源项目&#xff1a; https://github.com/liuhuanyong/QASystemOnMedicalKG 一.效果 二.需要安装&#xff1a; pip install pyahocorasick pip install py2neo 三.需要修改&#xff1a; 需要改的点&#xff1a; 1.改连接的方式 2.改读文件的方式 MedicalGraph 运行&am…