linux的守护进程

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_43743847/article/details/90382681

linux的守护进程本质是为服务器的后台做日志的 什么是日志呢 就是保留信息的 用数据库 比如学生的姓名 年龄等等 还有时间 许多数据库的table 并且不断的及时更新 由客户端的输入信息
linux的守护进程注意一点要彻底脱离当前父进程所在的控制终端和会话组 为了保持独立性
守护进程其实本质就是个孤儿进程 因为他在后台运行 我们在使用这个后台进程的时候 需要注意一个问题 因为父进程是进程组组长 所以不能进行创建新的会话组 sid ,所以只能是通过子进程进行创建守护进程

  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下必须显式等待子进程结束才能释放僵尸进程。

举个例子:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
/*#include <unistd.h>

int daemon(int nochdir, int noclose);

参数:

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

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

返回值:

成功:0

失败:-1

*/
#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();//创建守护线程
fd = open(“daemon.log”,O_WRONLY|O_CREAT|O_APPEND,0644);//创建并且打开守护线程日志文件
while(1){

    if(fd == -1)
        ERR_EXIT("open error");
  
    char *buf = asctime(localtime(&t));//localtime return struct tm* ::::::asctime return char*
    write(fd,buf,strlen(buf));
  
    sleep(1);
        break;
}
  close(fd);
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)//如果子进程已经是进程组的组长那么就不能创建脱离父进程的所属组 返回-1
ERR_EXIT(“SETSID ERROR”);
for(int i=0;i<3;++i)
{
close(i);
//open第一次重定向到i 并且返回i
open("/dev/null",O_RDWR);//关闭标准输入 标准输出 标准出错流 重定向到/dev/null这个垃圾桶 因为守护进程不需要显示信息到前台 也不需要标准输入信息 所以如果后台自动产生标准输入 直接把输入信息放到垃圾桶 防止内存被垃圾信息占用
}
chdir("/");//更改新进程的工作目录
umask(0);//设置文件掩码
return;
}

这里有个重定向到/dev/null的demon
#include<stdio.h>

#include<unistd.h>

#include<sys/stat.h>

#include<strings.h>

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
//重定向标准输入 输出 错误输出 到/dev/null垃圾桶 防止输出的东西占用资源 因为后台线程不需要

int main(void)

{

int i=0;


for(i;i<3;++i)

{

	close(i);

open("/dev/null",O_RDWR);

	

	

}

char buf[1024];

bzero(buf,sizeof(buf));

read(0,buf,sizeof(buf));

char buff[120]=“123”;

write(1,buff,sizeof(buff));



return 0;

}

展开阅读全文

没有更多推荐了,返回首页