特殊进程之守护进程

文章目录

  • 1、守护进程的概念
  • 2、如何查看守护进程
  • 3、编写守护进程的步骤
    • 3.1 创建子进程,父进程退出
    • 3.2 在子进程中创建新会话
    • 3.3 改变当前工作目录
    • 3.4 重设文件权限掩码
    • 3.5 关闭不需要的文件描述符
    • 3.6 某些特殊的守护进程打开/dev/null
  • 4、守护进程代码示例

1、守护进程的概念

守护进程(Daemon Process),也就是通常说的 Daemon 进程(精灵进程),是 Linux 中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。其他进程都是在用户登录或运行程序时创建,在运行结束或者用户注销时终止,但系统服务进程不受用户登录注销的影响,它们一直运行,这就是守护进程。

守护进程是个特殊的孤儿进程,这种进程脱离终端,为什么要脱离终端呢?之所以脱离于终端是为了避免进程被任何终端所产生的信息所打断,其在执行过程中的信息也不在任何终端上显示。由于在 Linux 中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭。

Linux 的大多数服务器就是用守护进程实现的。比如,Internet 服务器 inetd,Web 服务器 httpd 等。

2、如何查看守护进程

在终端上使用命令 ps axj

a:表示显示所有进程,包括其他用户的进程。
x:不仅可以显示有控制终端的进程,也可以显示没有控制终端的进程。
j:表示列出与作业控制相关的信息。

在这里插入图片描述

上述字段含义如下:
PPID:父进程ID。

PID:当前进程ID。

PGID:当前进程的进程组ID。

SID:会话ID。

TTY:该进程在哪个终端下运作,其中“?”表示与终端机无关,例如守护进程;tty1-tty6是本机上的登录者进程;pts/0等表示网络连接进主机的进程。

TPGID:终端进程组ID。

STAT:进程状态,其中“S”表示睡眠状态,“R”表示运行状态,“Z”表示僵尸状态,“T”表示停止状态,“W”表示等待状态。

UID:用户ID。

TIME:该进程使用的CPU时间。

COMMAND:正在运行的进程的命令名。

从上图中可以看出守护进程都有以下特点:

守护进程基本上都是以超级用户启动( UID 为 0 )
没有控制终端( TTY 为 ?)
终端进程组 ID 为 -1 ( TPGID 表示终端进程组 ID)

注意:COMMAND字段带有[ ]的叫内核守护进程,不带[ ]的叫普通守护进程,也叫做用户守护进程。

一般情况下,守护进程可以通过以下方式启动:

  1. 在系统启动时由启动脚本启动,这些启动脚本通常放在 /etc/rc.d 目录下
  2. 利用 inetd 超级服务器启动,如 telnet 等
  3. 由 cron 定时启动以及在终端用 nohup 启动的进程也是守护进程

这里面存放的基本都是守护进程的脚本
在这里插入图片描述

在Linux中,守护进程有两种方式,一种是svsy方式,一种是xinetd方式(超级守护进程)。 每个守护进程都会有一个脚本,可以理解成工作配置文件,守护进程的脚本需要放在指定位置,独立启动守护进程:放在/etc/rc.d 目录下,当然也包括xinet的shell脚本;超级守护进程:按照xinet中脚本的指示,它所管理的守护进程位于/etc/xinetd.config目录下。

sysv:

独立启动,一开机运行就会进入内存,一直处于listen状态,即使该守护进程不运行也会一直占用系统资源,但是其最大的优点就是,它一直启动,当有请求时会立即响应,响应速度快,比如http服务,这样的进程都保存在/etc/rc.d/init.d目录下

xinet d:

超级守护进程,管理众多的进程,比如telnet服务。xinetd自己是一个sysv,它就像老板一样,自己常驻于内存,管理其它的进程,其它进程就相当于它的员工,在其它进程没有用时会睡眠,并不占用系统资源,当有工作时候老板xinetd会通知它的员工,唤醒某个进程来执行作业。这种方式适合于那些不是经常被人使用,不需要常驻内存的程序,但是此方式响应时间长,但是节省系统资源,方便管理。超级守护进程的配置文件是/etc/xinetd.conf,超级守护进程的子进程们存放在/etc/xinetd.d/目录下

3、编写守护进程的步骤

3.1 创建子进程,父进程退出

由于守护进程是脱离控制终端的,因此完成第一步后子进程变成后台进程。之后的所有工作都在子进程中完成。而用户通过 shell 可以执行其他的命令,从而在形式上做到了与控制终端的脱离。

虽然父进程退出了,但是子进程也不是进程组的组长进程,因为父进程退出,子进程成为孤儿进程,接着子进程会被init进程给领养,成为init 进程的子进程

父进程先退出,子进程就会成为孤儿进程
子进程退出,父进程没有进行wait,子进程会成为僵尸进程

3.2 在子进程中创建新会话

这个步骤是创建守护进程中最重要的一步,在这里使用的函数是 setsid() 。

这里先要明确两个概念:进程组和会话期。

进程组

进程组是一个或多个进程的集合。进程组由进程组 ID 来唯一标识。除了进程号( PID )之外,进程组 ID 也是一个进程的必备属性。
每个进程组都有一个组长进程,其组长进程的进程号等于进程组 ID ,且进程组 ID 不会因组长进程的退出而受到影响。

会话期

会话期是一个或多个进程组的集合。通常一个会话开始于用户登录,终止于用户退出;或者说开始于终端打开,结束于终端关闭。会话期的第一个进程称为会话组长。在此期间该用户运行的所有进程都属于这个会话期。

进程组和会话期之间的关系如图:

在这里插入图片描述

setsid()函数说明
使用指令 man 2 setsid 查看详细信息

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

功能:
  如果调用进程不是进程组长,则 setsid() 将创建一个新会话。调用进程将成为新会话的会话组组长(即,其会话 ID 与其进程 ID 相同)。同时调用进程也将成为会话中新进程组的进程组组长(即,其进程组 ID 与其进程 ID 相同)。调用进程将是新进程组和新会话中的唯一进程。
参数:无
返回:
  成功:返回调用进程的(新)会话ID
  失败:返回(pid_t)-1,并设置 errno

上面已经提到,setsid() 函数用于创建一个新的会话,并担任该会话的组长,所以调用 setsid() 有下面 3 个作用

1、让进程摆脱原会话的控制
2、让进程摆脱原进程组的控制
3、让进程摆脱原控制终端的控制

由于在调用 fork() 函数时,子进程 全盘复制 了父进程的会话期进程组和控制终端等。所以虽然父进程退出了,但原先的 会话期、进程组、控制终端等并没有改变,因此,子进程并不是真正意义上的独立,而 setsid() 函数能够使进程完全独立出来,从而脱离所有其他进程的控制。

3.3 改变当前工作目录

使用 fork() 函数创建的子进程是完全继承了父进程的当前工作目录,所以从父进程继承过来的当前工作目录可能是一个挂载的文件系统中。因为守护进程有一般情况是在系统在引导之前是一直从在的,所以在进程工作的过程中当前目录所在的文件系统(比如“/mnt/usb” 等)是不能卸载的。

因此,一般的做法是将根目录作为守护进程的当前工作目录,这样就可以避免上述问题。当然,如有特殊需要,也可以把当前工作目录换成其他的路径,如“/tmp”。

改变工作目录的函数是 chdir() 函数,其函数原型如下所示:

#include <unistd.h>int chdir(const char *path);

功能:
  改变调用者的工作目录
参数:
  path:新的工作目录的路径
返回:
  成功:返回0
  失败:返回-1,同时设置errno

3.4 重设文件权限掩码

文件权限掩码(通常用八进制表示)的作用是屏蔽文件权限中的对应位。例如,如果文件权限掩码是0050,它表示屏蔽了文件所属用户组的可读与可执行权限。由于使用 fork() 函数新建的子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带来了一定的影响。如果守护进程需要创建文件,那么他可能需要设置特定的权限。因此,把文件权限掩码设置为一个已知的值(通常设置为0),可以增强该守护进程的灵活性。

umask的数值共有四位,例如上面的输出0050,四位数表示四组权限值,分别是文件特殊权限,文件所有者权限,文件所属用户组权限,其他用户权限。

这里我们先忽略掉文件特殊权限位。

可读权限r表示4,可写权限w表示2,可执行权限x表示1

umask值指的是需要从原始默认权限减掉的权限!我们已经知道r、w、x的数值分别是4、2、1。 所以如果要去掉可读和可执行权限,umask值中相应的位就是5

如果要去掉读权限,那就是4,去掉读与写权限,就是6,去掉执行与写权限,就是3,去掉写的权限,就是5!

新建文件和目录的默认权限值就是在原始默认权限的基础上去掉umask值,umask值与原始默认权限共同决定了新建文件和目录的默认权限值。

在使用open()建立新文件时, 该参数mode 并非真正建立文件的权限, 而是 (mode&~umask)的权限值。

设置文件权限掩码的函数是 umask()。在这里,通常的使用方法为 umask(0)。其函数原型如下所示:

#include <sys/types.h>
#include <sys/stat.h>mode_t umask(mode_t mask);

功能:
  umask() 将调用进程的文件模式创建掩码(umask)设置为 mask & 0777(即仅使用掩码的文件权限位)。
参数:
  mask:要设置的权限值,用八进制表示
返回:
  此系统调用始终成功,并返回掩码的上一个值。

3.5 关闭不需要的文件描述符

同样地,用 fork() 函数新建的子进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不会被守护进程访问,但它们一样占用系统资源,而且还可能导致所在的文件系统无法被卸载。

特别是守护进程和终端无关,所以指向终端设备的标准输入、标准输出和标准错误流等已经不再使用,应当被关闭。

可以使用函数 getdtablesize() 来获取当前进程文件描述符表的大小,并通过使用 close() 来依次关闭。

函数原型如下:

#include <unistd.h>
int getdtablesize(void);

getdtablesize()函数返回进程可以打开的最大文件数,比文件描述符的最大可能值多一个。

#include <unistd.h>
int close(int fd);

close() 用于关闭文件描述符,关闭成功则返回 0,失败则返回 -1 并设置 errno

所以关闭文件描述符的代码可以如下写法:

int num = getdtablesize(); // 获取当前进程文件描述符表大小for (int i = 0; i < num; i++)  
{close (i);
}

3.6 某些特殊的守护进程打开/dev/null

某些特殊的守护进程打开/dev/null,使其具有文件描述符0、1、2,这样任何一个试图读标准输入、标准输出、标准出错时都不会有任何效果,这样符合了守护进程不与终端设备相关联的属性。

/dev/null 是Linux下的黑洞文件,向里面写入的所有数据都将被忽略

4、守护进程代码示例

#include <stdio.h>     //for perror...
#include <string.h>    //for strlen...
#include <stdlib.h>    //for EXIT_FAILURE EXIT_SUCCESS...
#include <fcntl.h>     //for O_RDWR | O_CREAT | O_APPEND...
#include <unistd.h>    //for fork chidr setsid getdtablesize close...
#include <sys/types.h> //for umask...
#include <signal.h>    //for signal...volatile sig_atomic_t runing = 1;void sigint_handler(int sig)
{int fd = open("/tmp/dameon.log2", O_RDWR | O_CREAT | O_APPEND, 0644);char *p = "守护进程运行结束!\n";write(fd, p, strlen(p));close(fd);runing = 0;
}int main()
{// 创建子进程,父进程退出pid_t id = fork();if (id == -1){perror("fork");exit(EXIT_FAILURE);}if (id > 0) // 父进程{printf("父进程id:%d\n", getpid());exit(EXIT_SUCCESS);}//打印子进程号printf("子进程id:%d\n", getpid());// 在子进程中创建新会话pid_t temp_pid = setsid();// 改变当前的工作路径chdir("/");// 改变进程本身的umaskumask(0);int num = getdtablesize(); /* 获取当前进程文件描述符表大小 */int i = 0;for (i = 0; i < num; i++){close(i);}// 屏蔽一些控制终端操作的信号signal(SIGTTOU, SIG_IGN);signal(SIGTTIN, SIG_IGN);signal(SIGTSTP, SIG_IGN);signal(SIGHUP, SIG_IGN);// 对SIGINT进行捕获signal(SIGINT, sigint_handler);while (runing){int fd = open("/tmp/dameon.log", O_RDWR | O_CREAT | O_APPEND, 0644);if (fd == -1){perror("open");exit(EXIT_FAILURE);}char *p = "这个一个守护进程!\n";write(fd, p, strlen(p));close(fd);sleep(3);}return 0;
}

编译然后运行

在这里插入图片描述

查看对应的日志文件

在这里插入图片描述

向这个进程发送2号信号,进程则会捕获到2号信号,触发自定义函数,再次查看进程,发现进程已经结束

在这里插入图片描述

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

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

相关文章

[UNILM]论文实现:Unified Language Model Pre-training for Natural Language.........

文章目录 一、完整代码二、论文解读2.1 介绍2.2 架构2.3 输入端2.4 结果 三、过程实现四、整体总结 论文&#xff1a;Unified Language Model Pre-training for Natural Language Understanding and Generation 作者&#xff1a;Li Dong, Nan Yang, Wenhui Wang, Furu Wei, Xia…

MyBatis--07--启动过程分析、SqlSession安全问题、拦截器

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 谈谈MyBatis的启动过程具体的操作过程如下&#xff1a;实现测试类,并测试SqlSessionFactorySqlSession SqlSession有数据安全问题?在MyBatis中&#xff0c;SqlSess…

vuex如何存储数据、获取数据、以及数据的持久化

前提必须已经在vue中安装了vuex插件不然无法使用&#xff0c;不知道怎么创建vue和安装vuex的可以看这个视频&#xff0c;node.js版本最好16以上不然可能会安装失败&#xff1a;30分钟学会Vue之VueRouter&Vuex 趁着暑假掌握一门技能 大学生前端实习毕业设计必备技能_哔哩哔哩…

好代码资源网整站打包代码(包含了最新数据),集成了深度二开的ripro主题,非常适合做资源网站创业用

好代码资源网是基于wordpress开发的一个资源分享类网站&#xff0c;在开发者圈子里还算小有名气&#xff0c;这里分享婴整站打包代码&#xff08;包含了最新数据&#xff09;。网站本身集成了深度二开的ripro主题&#xff0c;非常适合做资源网站创业用。 资源下载类网站目前还…

Button背景颜色改不了,一直是默认的紫色

使用android.widget.Button <android.widget.Buttonandroid:layout_width"wrap_content"android:layout_height"wrap_content"android:onClick"doClick"android:text"这是一个按钮"android:textColor"color/black"androi…

kubesphere安装后启用DevOps

官方文档&#xff1a;KubeSphere DevOps 系统 1、集群管理---定制资源定义 进入目录&#xff1a;集群管理---定制资源定义搜索&#xff1a;clusterconfiguration 点击 ks-installer 右侧的 &#xff0c;选择编辑 YAML 在该 YAML 文件中&#xff0c;搜索 devops&#xff0c;…

No CUDA GPUs are available

文章目录 前言尝试方法一、尝试方法一二、尝试方法二 总结 前言 之前用服务器跑的时候&#xff0c;发现是可以跑的。但当有其他人一同使用的时候&#xff0c;就会抛出&#xff1a;No CUDA GPUs are available&#xff0c;这个时候我尝试了以下两种方式解决&#xff0c;后面终于…

一到冬天,助听器出现声音小、无声、时有时无……

冬天是一个寒冷干燥的季节&#xff0c;对于助听器的使用者来说&#xff0c;也是一个需要特别注意保养的季节。助听器是高精密的电子产品&#xff0c;如果不注意保养&#xff0c;可能会出现声音小、无声、时有时无等故障&#xff0c;影响听力康复的效果。那么&#xff0c;冬天我…

C++中string类的使用

目录 一.string类 1.1为什么学习string类&#xff1f; 1.2.标准库中的string类 二.string对象的元素访问 2.1.1使用operator[]与at实现访问 2.1.2正向迭代器访问 2.1.3反向迭代器访问 2.1.4const正向迭代器&#xff08;不能修改&#xff09; 2.1.5const反向迭代器&#…

垃圾收集算法和各种垃圾收集器的实现

深入理解Jvm虚拟机第三章 二、对象已死&#xff1f;3.2.1 引用计数算法3.2.2 可达性分析算法3.2.3 再谈引用3.2.4 生存还是死亡3.2.5 回收方法区 三、垃圾收集算法3.3.1 分代收集理论3.3.2 标记-清除算法3.3.3 标记-复制算法3.3.4 标记-整理算法 四、HotSpot的算法细节实现3.4.…

C# WPF上位机开发(串口界面设计)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 如果只是把上位机看成是纯软件开发&#xff0c;本身不和硬件打交道的话&#xff0c;那么这就把上位机的操作范围给限定死了。事实上&#xff0c;上…

数据库系统概论期末经典大题讲解(范式提升、求闭包、求主码)

上一次我们介绍了数据库中关系代数查询&#xff0c;从选择、投影到连接等操作符&#xff0c;探索了数据库查询 大家可以移步我的文章&#xff1a;数据库系统概论期末经典大题讲解&#xff08;用关系代数进行查询&#xff09;-CSDN博客 今天&#xff0c;我们将继续沿着数据库系统…

《python每天一小段》--12 数据可视化《1》

欢迎阅读《Python每天一小段》系列&#xff01;在本篇中&#xff0c;将使用Python Matplotlib实现数据可视化的简单图形。 一、概念 Matplotlib是一个流行的Python数据可视化库&#xff0c;它提供了丰富的绘图功能&#xff0c;可以创建各种类型的图表&#xff0c;包括折线图、…

Spring框架学习:Bean生命周期

目录 SpringBean的生命周期 Bean实例属性填充 三级缓存 常用的Aware接口 Spring IoC容器实例化Bean总结 SpringBean的生命周期 Spring Bean的生命周期是从 Bean 实例化之后&#xff0c;即通过反射创建出对象之后&#xff0c;到Bean成为一个完整对象&#xff0c;最终存储到…

【MyBatis系列】MyBatis字符串问题

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

SpringBoot + Spring Cloud Alibaba + Nacos实现服务管理

1、参考文档 Spring Cloud Alibaba参考文档 https://spring-cloud-alibaba-group.github.io/github-pages/hoxton/zh-cn/index.html Spring Cloud Alibaba官方文档 https://github.com/alibaba/spring-cloud-alibaba/wiki/ 2、引入 Alibaba 依赖 每个 SpringBoot 都有对应的…

javascript实现Stack(栈)数据结构

上一篇文章我们理解了List这种数据结构&#xff0c;知道了它的特点和一些使用场景&#xff0c;这篇文章我们就来看一下栈这种数据结构&#xff0c;这里的栈可不是客栈哦&#xff0c;哈哈 栈其实和List非常像&#xff0c;使用javascript实现都是基于数组来实现 尝试理解Stack …

windows系统和虚拟机上ubuntu系统通过虚拟串口进行通信

本文的目的是实现windows系统和虚拟机上安装的ubuntu通过串口进行通信。为了直观观测串口收发数据的内容&#xff0c;需要在windows系统和ubuntu系统使用串口助手来进行监听。windows系统端用的监听工具是串口助手SSCOM&#xff0c;ubuntu系统端使用的串口助手是CuteCom。 ubu…

OpenCL学习笔记(一)开发环境搭建(win10+vs2019)

前言 异构编程开发&#xff0c;在高性能编程中有重要的&#xff0c;笔者本次只简单介绍下&#xff0c;如何搭建简单的开发环境&#xff0c;可以供有需要的小伙伴们开发测试使用 一、获取opencl的sdk库 1.使用cuda库 若本机有Nvidia的显卡&#xff0c;在安装cuda库后&#x…

如何提高大模型在超长上下文的表现?Claude实验表明加一句prompt立即提升效果~

本文来自DataLearnerAI官方网站&#xff1a;如何提高大模型在超长上下文的表现&#xff1f;Claude实验表明加一句prompt立即提升效果~ | 数据学习者官方网站(Datalearner)https://www.datalearner.com/blog/1051701947131881 Claude 2.1版本的模型上下文长度最高拓展到200K&am…