linux系统编程之进程(八):守护进程详解及创建,daemon()使用


一,守护进程概述

Linux Daemon(守护进程)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。它不需要用户输入就能运行而且提供某种服务,不是对整个系统就是对某个用户程序提供服务。Linux系统的大多数服务器就是通过守护进程实现的。常见的守护进程包括系统日志进程syslogd、 web服务器httpd、邮件服务器sendmail和数据库服务器mysqld等。

守护进程一般在系统启动时开始运行,除非强行终止,否则直到系统关机都保持运行。守护进程经常以超级用户(root)权限运行,因为它们要使用特殊的端口(1-1024)或访问某些特殊的资源。

一个守护进程的父进程是init进程,因为它真正的父进程在fork出子进程后就先于子进程exit退出了,所以它是一个由init继承的孤儿进程。守护进程是非交互式程序,没有控制终端,所以任何输出,无论是向标准输出设备stdout还是标准出错设备stderr的输出都需要特殊处理。

守护进程的名称通常以d结尾,比如sshd、xinetd、crond等

二,创建守护进程步骤

首先我们要了解一些基本概念:

进程组 :

  • 每个进程也属于一个进程组
  • 每个进程主都有一个进程组号,该号等于该进程组组长的PID号 .
  • 一个进程只能为它自己或子进程设置进程组ID号

会话期:

会话期(session)是一个或多个进程组的集合。

setsid()函数可以建立一个对话期:

 如果,调用setsid的进程不是一个进程组的组长,此函数创建一个新的会话期

(1)此进程变成该对话期的首进程

(2)此进程变成一个新进程组的组长进程。

(3)此进程没有控制终端,如果在调用setsid前,该进程有控制终端,那么与该终端的联系被解除。 如果该进程是一个进程组的组长,此函数返回错误。

(4)为了保证这一点,我们先调用fork()然后exit(),此时只有子进程在运行

现在我们来给出创建守护进程所需步骤:

编写守护进程的一般步骤步骤:

(1)在父进程中执行fork并exit推出;

(2)在子进程中调用setsid函数创建新的会话;

(3)在子进程中调用chdir函数,让根目录 ”/” 成为子进程的工作目录;

(4)在子进程中调用umask函数,设置进程的umask为0;

(5)在子进程中关闭任何不需要的文件描述符

说明:

1. 在后台运行。
为避免挂起控制终端将Daemon放入后台执行。方法是在进程中调用fork使父进程终止,让Daemon在子进程中后台执行。
if(pid=fork())
exit(0);//是父进程,结束父进程,子进程继续
2. 脱离控制终端,登录会话和进程组
有必要先介绍一下Linux中的进程与控制终端,登录会话和进程组之间的关系:进程属于一个进程组,进程组号(GID)就是进程组长的进程号(PID)。登录会话可以包含多个进程组。这些进程组共享一个控制终端。这个控制终端通常是创建进程的登录终端。
控制终端,登录会话和进程组通常是从父进程继承下来的。我们的目的就是要摆脱它们,使之不受它们的影响。方法是在第1点的基础上,调用setsid()使进程成为会话组长:
setsid();
说明:当进程是会话组长时setsid()调用失败。但第一点已经保证进程不是会话组长。setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性,进程同时与控制终端脱离。
3. 禁止进程重新打开控制终端
现在,进程已经成为无终端的会话组长。但它可以重新申请打开一个控制终端。可以通过使进程不再成为会话组长来禁止进程重新打开控制终端:
if(pid=fork())
exit(0);//结束第一子进程,第二子进程继续(第二子进程不再是会话组长)
4. 关闭打开的文件描述符
进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。按如下方法关闭它们:
for(i=0;i 关闭打开的文件描述符close(i);>
5. 改变当前工作目录
进程活动时,其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录。对于需要转储核心,写运行日志的进程将工作目录改变到特定目录如/tmpchdir("/")
6. 重设文件创建掩模
进程从创建它的父进程那里继承了文件创建掩模。它可能修改守护进程所创建的文件的存取位。为防止这一点,将文件创建掩模清除:umask(0);
7. 处理SIGCHLD信号
处理SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将SIGCHLD信号的操作设为SIG_IGN。
signal(SIGCHLD,SIG_IGN);
这样,内核在子进程结束时不会产生僵尸进程。这一点与BSD4不同,BSD4下必须显式等待子进程结束才能释放僵尸进程。

三,创建守护进程

在创建之前我们先了解setsid()使用:

  #include <unistd.h>

       pid_t setsid(void);

DESCRIPTION
       setsid()  creates a new session if the calling process is not a process
       group leader
The calling process is the leader of  the  new  session,
       the  process group leader of the new process group, and has no control-
       ling tty
The process group ID and session ID of the  calling  process
       are set to the PID of the calling process
The calling process will be
       the only process in this new process group and in this new session
.

//调用进程必须是非当前进程组组长,调用后,产生一个新的会话期,且该会话期中只有一个进程组,且该进程组组长为调用进程,没有控制终端,新产生的group ID 和 session ID 被设置成调用进程的PID

RETURN VALUE
       On success, the (new) session ID of the calling  process  is  returned.
       On  error,  (pid_t) -1  is  returned,  and errno is set to indicate the
       error.

现在根据上述步骤创建一个守护进程:

以下程序是创建一个守护进程,然后利用这个守护进程每个一分钟向daemon.log文件中写入当前时间

复制代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>#define ERR_EXIT(m) \
do\
{\perror(m);\exit(EXIT_FAILURE);\
}\
while (0);\void creat_daemon(void);
int main(void)
{time_t t;int fd;creat_daemon();while(1){fd = open("daemon.log",O_WRONLY|O_CREAT|O_APPEND,0644);if(fd == -1)ERR_EXIT("open error");t = time(0);char *buf = asctime(localtime(&t));write(fd,buf,strlen(buf));close(fd);sleep(60);}return 0;
}
void creat_daemon(void)
{pid_t pid;pid = fork();if( pid == -1)ERR_EXIT("fork error");if(pid > 0 )exit(EXIT_SUCCESS);if(setsid() == -1)ERR_EXIT("SETSID ERROR");chdir("/");int i;for( i = 0; i < 3; ++i){close(i);open("/dev/null", O_RDWR);dup(0);dup(0);}umask(0);return;
}
复制代码

结果:

QQ截图20130713184143

结果显示:当我一普通用户执行a.out时,进程表中并没有出现新创建的守护进程,但当我以root用户执行时,成功了,并在/目录下创建了daemon.log文件,cat查看后确实每个一分钟写入一次。为什么只能root执行,那是因为当我们创建守护进程时,已经将当前目录切换我/目录,所以当我之后创建daemon.log文件是其实是在/目录下,那肯定不行,因为普通用户没有权限,或许你会问那为啥没报错呢?其实是有出错,只不过我们在创建守护进程时已经将标准输入关闭并重定向到/dev/null,所以看不到错误信息。

四,利用库函数daemon()创建守护进程

其实我们完全可以利用daemon()函数创建守护进程,其函数原型:

#include <unistd.h>

int daemon(int nochdir, int noclose);


DESCRIPTION
       The daemon() function is for programs wishing to detach themselves from
       the controlling terminal and run in the background as system daemons.

       If nochdir is zero, daemon()  changes  the  process’s  current  working
       directory to the root directory ("/"); otherwise,

       If  noclose is zero, daemon() redirects standard input, standard output
       and standard error to /dev/null; otherwise,  no  changes  are  made  to
       these file descriptors.

功能:创建一个守护进程

参数:

nochdir:=0将当前目录更改至“/”

noclose:=0将标准输入、标准输出、标准错误重定向至“/dev/null”

返回值:

成功:0

失败:-1

现在我们利用daemon()改写刚才那个程序:

复制代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>#define ERR_EXIT(m) \
do\
{\perror(m);\exit(EXIT_FAILURE);\
}\
while (0);\void creat_daemon(void);
int main(void)
{time_t t;int fd;if(daemon(0,0) == -1)ERR_EXIT("daemon error");while(1){fd = open("daemon.log",O_WRONLY|O_CREAT|O_APPEND,0644);if(fd == -1)ERR_EXIT("open error");t = time(0);char *buf = asctime(localtime(&t));write(fd,buf,strlen(buf));close(fd);sleep(60);}return 0;
}
复制代码

当daemon(0,0)时:

QQ截图20130713190523

结果同刚才一样,也是只有root才能成功,普通用户执行时看不到错误信息

现在让daemon(0,1),就是不关闭标准输入输出结果:

QQ截图20130713190932

可以看到错误信息

现在让daemon(1,0),就是不重定向,结果如下:

QQ截图20130713191221

这次普通用户执行成功了,以为没有切换到/目录下,有权限

其实我们可以利用我们刚才写的创建守护进程程序默认daemon()实现:

代码如下:

复制代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>#define ERR_EXIT(m) \
do\
{\perror(m);\exit(EXIT_FAILURE);\
}\
while (0);\void creat_daemon(int nochdir, int noclose);
int main(void)
{time_t t;int fd;creat_daemon(0,0);while(1){fd = open("daemon.log",O_WRONLY|O_CREAT|O_APPEND,0644);if(fd == -1)ERR_EXIT("open error");t = time(0);char *buf = asctime(localtime(&t));write(fd,buf,strlen(buf));close(fd);sleep(60);}return 0;
}
void creat_daemon(int nochdir, int noclose)
{pid_t pid;pid = fork();if( pid == -1)ERR_EXIT("fork error");if(pid > 0 )exit(EXIT_SUCCESS);if(setsid() == -1)ERR_EXIT("SETSID ERROR");if(nochdir == 0)chdir("/");if(noclose == 0){int i;for( i = 0; i < 3; ++i){close(i);open("/dev/null", O_RDWR);dup(0);dup(0);}umask(0);return;
}
复制代码

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

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

相关文章

您还在调试吗?

调试是“以交互方式运行程序/方法&#xff0c;在每个语句后中断执行流程并显示……的过程。”简而言之&#xff0c;它是一种非常有用的技术……对于一个糟糕的程序员而言。 或仍然在用C编写过程代码的老程序员。面向对象的程序员从不调试其代码-他们编写单元测试。 我的意思是&…

RESTful规范

本文目录 什么是RESTful RESTful API设计 基于Django实现 什么是RESTful REST与技术无关&#xff0c;代表的是一种软件架构风格&#xff0c;REST是Representational State Transfer的简称&#xff0c;中文翻译为“表征状态转移”REST从资源的角度类审视整个网络&#xff0c;它…

反向输出dna序列_蛋白质序列反向(逆向)翻译成DNA序列-在线工具

请粘贴蛋白质序列&#xff0c;如果需要输入多个序列&#xff0c;请以fasta格式输入&#xff0c;输入总长度不超过2万个字符。>testACDEFGHIKLMNPQRSTVWY*推荐使用IE 8.0以上、chrome或者Firefox等浏览器。请输入该蛋白来源物种的密码子使用表(GCG格式)&#xff0c;下表示大肠…

子函数的指针释放问题

C语言中遇到一个这样的问题&#xff1a;子函数中malloc了一个指针存储数据&#xff0c;作为该子函数的返回值&#xff0c;return到主函数。那么这个指针应该在哪里释放呢&#xff1f;显然不能在子函数里释放&#xff0c;否则返回值没有意义。这样就应该在主函数里释放&#xff…

利用cookie模拟登陆知乎

我们知道一些网站是需要账号密码才可以登陆的&#xff0c;例如知乎。而利用requests库里的get方法的headers参数可以达到这个目的 首先在知乎的网页上登陆自己的知乎账号&#xff0c;利用chrome的开发者工具&#xff08;F12&#xff09;可以捕获我们的get方法向浏览器提供的coo…

linux中fork()函数详解(原创!!实例讲解)

一、fork入门知识 一个进程&#xff0c;包括代码、数据和分配给进程的资源。fork&#xff08;&#xff09;函数通过系统调用创建一个与原来进程几乎完全相同的进程&#xff0c;也就是两个进程可以做完全相同的事&#xff0c;但如果初始参数或者传入的变量不同&#xff0c;两个进…

C++ vector的释放

项目上用到vector容器&#xff0c;没有手动释放&#xff0c;总是会在这里出现内存分配不成功的问题&#xff0c;因此对vector的释放了解了一下。初始代码如下&#xff1a; vector <float*> dets(nTotalLayers); //dets : 记录每层图像的 Hessian 行列式&#xff1b; for …

设计模式 工厂方法_使用工厂方法模式设计最佳实践

设计模式 工厂方法在前面的“设计模式”示例中&#xff0c;我们解释了当今常用的“工厂”模式。 在本节中&#xff0c;我们将了解具有更多抽象的更高级的解决方案。 该模式称为工厂方法设计模式。 定义&#xff1a; Factory方法模式提供了一种用于创建对象的方法&#xff0c;…

C Programming Language

代做module作业、代做C/C编程设计作业、代写Programming Language作业、代做C/C课程设计作业C Programming LanguageContribution to module (weighting: 20 %)1st Semester 2018-2019Out: WED. 5th Dec. 2018 Due: 18:00[GMT], WED. 19th Dec. 2018Main objective of the assi…

python修改列表中字典内的值_python修改字典内key对应值的方法

python学习笔记&#xff1a;字典python版本&#xff1a;Python 2.6.6系统环境&#xff1a;CentOS release 6.2 x86_64本文参考了互联网上前辈的一些文章一、字典是python中最灵活的内置数据结构类型&#xff0c;如果把列表看作是有序的对象集合&#xff0c;那么字典就是无序的集…

MATLAB使用技巧

1、ctrl c 或者 ctrl break 强行中断程序运行 2、变量X 右键save as为DX后&#xff0c;再次使用时load有区别&#xff1a;load(DX.mat) 得到的是X这个变量&#xff0c;直接出现在workspace里&#xff1b;Xnew load(DX.mat)得到的是一个名称为Xnew的结构体&#xff0c;里面包含…

Beta 冲刺 (2/7)

团队信息 队名&#xff1a;爸爸饿了组长博客&#xff1a;here作业博客&#xff1a;here组员情况 组员1&#xff08;组长&#xff09;&#xff1a;王彬 过去两天完成了哪些任务 完成考试确定历史记录页面与排行榜页面的前端页面风格接下来的计划 & 还剩下哪些任务 各个食堂平…

您真的需要instanceof吗?

使用instanceof是一种代码味道。 我认为我们可能对此表示同意。 每当我看到这样的构造时&#xff0c;我肯定会出现问题。 也许有人只是在进行更改时没有注意到问题&#xff1f; 也许有一个主意&#xff0c;但是它太复杂了&#xff0c;以至于需要太多的精力或时间才能让开发人员…

python 累积正态分布函数_Python编程基础—Python语句书写规范

Python语句中没有专门的“结束符”。Python解释器不是根据"结束符"来判断语句是否结束,而是根据语法的完整性来判断。一、Python语句编写规则①通常是一行一句x1 1 x2 2 x3 3 print(x1,x2,x3)②也可以一行多句&#xff0c;用语句分隔符“;”对两个语句进行标识x1 …

MATLAB批量改变图片大小

%2018年6月28日11:07:15 %把一个目录下的图片缩放到指定大小 clc clear ratio 0.2;%缩放比例 cd(F:\数据集\crumpled clothes\cloth3\original image\);%不加这句话找不到图片 dpath F:\数据集\crumpled clothes\cloth3\original image\*.JPG;%找到路径下所有格式为.JPG的文件…

基于Libevent的HTTP Server

简单的Http Server 使用Libevent内置的http相关接口&#xff0c;可以很容易的构建一个Http Server&#xff0c;一个简单的Http Server如下&#xff1a; #include <event2/event.h> #include <event2/buffer.h> #include <event2/http.h> #include <Winso…

python写入数据的一种措施_Python 文件数据读写的具体实现

文件数据读写读写文件&#xff0c;本质上是请求操作系统打开一个文件对象&#xff0c;然后&#xff0c;通过操作系统提供的接口从这个文件对象中读取数据(读文件)&#xff0c;或者把数据写入这个文件对象(写文件)。文件读取使用 Python 内置 open() 函数&#xff0c;以 rt 的模…

MATLAB的dir函数

1、作用获得指定文件夹下的所有子文件夹和文件,并存放在在一种文件结构体数组中. 2.使用方法dir(.)列出当前目录下所有子文件夹和文件dir(G:\Matlab)列出指定目录下所有子文件夹和文件dir(G:\Matlab\*.jpg)列出当前目录下符合正则表达式的文件夹和文件3.例如&#xff1a;列出文…

libevent简介和使用

libevent是一个基于事件触发的网络库&#xff0c;memcached底层也是使用libevent库&#xff0c;今天学习下。总体来说&#xff0c;libevent有下面一些特点和优势&#xff1a;* 统一数据源&#xff0c; 统一I/O事件&#xff0c;信号和定时器这三种事件&#xff1b;* 可移植&…

qr码生成_从Java程序生成QR码图像

qr码生成如果您精通技术和小工具&#xff0c;则必须了解QR码。 这些天&#xff0c;到处都可以找到它-在博客&#xff0c;网站&#xff0c;甚至在某些公共场所。 这在移动应用程序中非常流行&#xff0c;在移动应用程序中&#xff0c;您可以使用QR Code扫描仪应用程序扫描QR Cod…