当前位置: 首页 > news >正文

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进程会尝试重新启动该进程,以确保系统稳定运行。

相关文章:

Linux常见进程类别

目录 常见进程类别 守护进程&精灵进程 任务管理 进程组 作业 作业 | 进程组 会话 w命令 守护进程 守护进程的创建 setsid()函数 daemon()函数 模拟实现daemon函数 前台进程 | 后台进程 僵尸进程 | 孤儿进程 僵尸进程的一些细节 守护进程 | 后台进程 守护…...

智能小车之蓝牙控制并测速小车、wife控制小车、4g控制小车、语音控制小车

目录 1. 蓝牙控制小车 2. 蓝牙控制并测速小车 3. wifi控制测速小车 4. 4g控制小车 5. 语音控制小车 1. 蓝牙控制小车 使用蓝牙模块&#xff0c;串口透传蓝牙模块&#xff0c;又叫做蓝牙串口模块 串口透传技术&#xff1a; 透传即透明传送&#xff0c;是指在数据的传输过…...

指针进阶(一)

指针进阶 1. 字符指针面试题 2. 指针数组3. 数组指针3.1 数组指针的定义3.2 &数组名VS数组名 3.3 数组指针的使用4. 数组传参和指针传参4.1 一维数组传参4.2 二维数组传参4.3 一级指针传参4.4 二级指针传参 前言 指针的主题&#xff0c;我们在初级阶段的《指针》章节已经接…...

c# sql 判断表中是否包含指定字段

你可以使用以下方法来判断一个 SQL 数据库中的表是否包含指定的字段。 首先&#xff0c;你需要连接到数据库&#xff0c;然后执行一条 SQL 查询语句来检查表结构。你可以使用 SELECT 语句和 INFORMATION_SCHEMA.COLUMNS 系统视图来获取表中的所有列信息。 下面是一个示例代码…...

08-JVM垃圾收集器详解

上一篇&#xff1a;07-垃圾收集算法详解 如果说收集算法是内存回收的方法论&#xff0c;那么垃圾收集器就是内存回收的具体实现。 虽然我们对各个收集器进行比较&#xff0c;但并非为了挑选出一个最好的收集器。因为直到现在为止还没有最好的垃圾收集器出现&#xff0c;更加没…...

sql_mode详解

文章目录 一、sql_mode作用二、查询sql_mode三、mysql8默认的mode配置&#xff08;6个默认配置&#xff09;四、常见mode详细解释mysql8默认配置了的mode&#xff08;6个&#xff09;需要自己配置的mode&#xff08;4个&#xff09; 五、设置sql_mode&#xff08;一旦设置了&am…...

Vue3的新特性总结

一、Vue3 里 script 的三种写法 首先&#xff0c;Vue3 新增了一个叫做组合式 api 的东西&#xff0c;英文名叫 Composition API。因此 Vue3 的 script 现在支持三种写法。 1、最基本的 Vue2 写法 <template><div>{{ count }}</div><button click"…...

【Node】Mac多版本Node切换

1、查看当前电脑是否安装node node -v或者查看当前电脑通过brew安装的node路径 ls /usr/local/Cellar/node*2、查看可安装的node brew search node3、安装其他版本node 下载需要安装的node版本 brew install node144、brew切换node版本 假设之前的版本是18&#xff0c;需…...

Apache POI

POI介绍 Apache POI是用Java编写的免费开源的跨平台的Java API&#xff0c; Apache POI提供API给Java程序对Microsoft Office格式档案读和写的功能&#xff0c; 其中使用最多的就是使用POI操作Excel文件。 maven坐标&#xff1a; <dependency><groupId>org.apa…...

个人能做股票期权吗?个人期权交易开户条件新规

个人投资者是可以交易股票期权的&#xff0c;不过期权交易通常需要投资者具备一定的投资经验和风险承受能力&#xff0c;因为期权交易涉及较高的风险和复杂性&#xff0c;下文为大家介绍个人能做股票期权吗&#xff1f;个人期权交易开户条件新规的内容。本文来自&#xff1a;期…...

Java面试整理(一)

开篇 面试,应该都是打工人需要面对的事情。我记得自己以前开始准备Java工程师面试时,都会去看那个《面试宝典》,当时这个“宝典”真的很经典,现在应该还是不少朋友会看这个。我自己经历过了找工作的面试,和企业招聘工作。所以我自己更加想从这两个不同的角度去和大家交流这…...

国家信息中心举办“数字政府建设暨数字安全技术”研讨会:海云安提出数字政府软件供应链安全解决方案

近日&#xff0c;由国家信息中心主办&#xff0c;复旦大学研究院承办的“数字政府建设暨数字安全技术研讨会”在义乌顺利召开。国家信息中心信息与网络安全部副主任禄凯&#xff0c;复旦大学党委常委、宣传部部长陈玉刚&#xff0c;义乌市委常委、常务副市长喻新贵为会议致辞。…...

uniapp 处理 分页请求

我的需求是手机上一个动态滚动列表&#xff0c;下拉到底部时&#xff0c;触发分页数据请求 uniapp上处理分页解决方案 主要看你是如何写出滚动条的。我想到的目前有三种 &#xff08;1&#xff09;页面滚动&#xff1a;直接使用onReachBottom方法&#xff0c;可以监听到达底部…...

最新2米分辨率北极开源DEM数据集(矢量文件)

一、项目背景 美国明尼苏达大学(University of Minnesota)的极地地理空间中心(Polar Geospatial Center, PGC)于2023年8月发布了北极数字高程模型4.1版本(ArcticDEM Mosaic 4.1)。该DEM数据集是革命性的&#xff0c;分辨率达到了2米&#xff0c;而一般的开源DEM数据集分辨率是3…...

【计算机网络】HTTP(下)

本文承接上文的代码进行改造&#xff0c;上文链接&#xff1a;HTTP上 文章目录 1. 实现网站跳转实现 自己的网站跳转 2. 请求方法(get) && 响应方法(post)GET方法POST方法GET与POST的应用场景 3. HTTP状态码在自己设计的代码中发现4043开头的状态码(重定向状态码)永久…...

自学Python03-学会Python中的while循环语句

我们来学习一下怎么使用列表和字典吧&#xff01; 1.列表 首先&#xff0c;我们来学习一下列表。列表是一个有序的集合&#xff0c;它可以包含任何类型的数据&#xff0c;比如数字、字符串或其他列表。我们可以用方括号 [] 来创建一个列表&#xff0c;用逗号分隔各个元素。 …...

PatchMatchNet 学习笔记 译文 深度学习三维重建

9 PatchMatchNet CVPR-2021 patchmatchnet源码下载 PatchMatchNet 代码注释版 下载链接(注释非常详细,较源码结构有调整,使用起来更方便) PatchMatchNet-CVPR-2021(源码、原文+注释+译文+批注) 9.0 主要特点 金字塔,基于传统的PatchMatch算法,精度高,速度快 Pa…...

为什么要使用设计模式,以及使用设计模式的好处

在软件开发中&#xff0c;衡量软件质量只要包含如下指标&#xff1a; 正确性可维护性可读性可扩展性简洁性可测试性健壮性灵活性可复用性 然而&#xff0c;对于一些刚入行的新程序员来说&#xff0c;往往会注意不到上面这些问题&#xff0c;从而产生了一些让人头皮发麻的烂代…...

【Springcloud】Sentinel熔断和降级

【Springcloud】Sentinel熔断和降级 【一】基本介绍【1】什么是熔断和降级【2】为什么使用熔断和降级【3】Sentinel熔断和降级【4】核心概念 【二】下载方式【1】Windows平台安装包下载【2】打开控制台 【三】使用案例【1】添加依赖【2】添加Sentinel配置【3】添加TestUserCont…...

javascript实战开发:json数据求指定元素的和算法

项目需求 在js中&#xff0c;格式如&#xff1a; [{"name": "一(0)班-电量,一(9)班-电流,一(9)班-功率","odata": {"prev_0_day_val_diff": "10.189941,-3.0,79.0",} },{"name": "一(10)班-电量,一(10)班-…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

基于Flask实现的医疗保险欺诈识别监测模型

基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施&#xff0c;由雇主和个人按一定比例缴纳保险费&#xff0c;建立社会医疗保险基金&#xff0c;支付雇员医疗费用的一种医疗保险制度&#xff0c; 它是促进社会文明和进步的…...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中&#xff0c;接口是一种抽象类型&#xff0c;它定义了一组方法的集合&#xff1a; // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的&#xff1a; // 矩形结构体…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

C# 类和继承(抽象类)

抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

docker 部署发现spring.profiles.active 问题

报错&#xff1a; org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...