守护进程 类守护进程

防止产生僵尸进程的方法:

1、用信号,父进程接受子进程的退出信号,调用wait,附件1

2、fork两次使进程变成孤儿进程。附件2  

————————————————————

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


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

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

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

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

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

说明:

1. 在后台运行。

2. 脱离控制终端,登录会话和进程组 

3. 禁止进程重新打开控制终端 

4. 关闭打开的文件描述符

5. 改变当前工作目录 

6. 重设文件创建掩模

7. 处理SIGCHLD信号 

方法二:调用库函数daemon()

把普通进程变为类似守护进程的方法:

1、当$shopt | grep huponexit显示为off  //关闭终端(session),SIGHUP不会被后台接受

$ node server.js > stdout.txt 2> stderr.txt < /dev/null &  //io重定向,后台运行

"后台任务"与"前台任务"的本质区别只有一个:是否继承标准输入。所以,执行后台任务的同时,用户还可以输入其他命令。

2、当$shopt | grep huponexit显示为on

$ node server.js > stdout.txt 2> stderr.txt < /dev/null &  //io重定向,后台运行
$ disown    // 移出jobs,附件3

***3、$ nohup node server.js &
4、另一种思路是使用 terminal multiplexer (终端复用器:在同一个终端里面,管理多个session),典型的就是 Screen 命令和 Tmux 命令

5、Node 工具

***6、Systemd

让进程在后台可靠运行的几种方法(a断开连接后继续运行,b已经运行了补救,c大量这样的程序简化操作):

1、a、nohup/setsid/&

setsid ping 192.168.117  //PPID = 1
nohup ping 192.168.117 &
(ping 192.168.7.117 &)   //PPID = 1
2、b、disdown

2-1、

  $wget -c ftp:***  &;
  $disown -ah; 或者$ jobs $ diswon -h %1
  $ps -ef | grep wget


2-2、

  $cp largefile/   a/
   Ctrl + z
  $jobs   //看作业号
  $bg %1
  $jobs
  $disown -h %1
  $ ps -ef | grep cp

3、c、screen

$screen -dmS Urumchi   //建立一个处于断开模式下的会话
$screen -list         //列出所有会话
$screen -r Urumchi     //重新连接指定会话
$pstree -H 9499       //PPID = 1
用快捷键CTRL-a d 来暂时断开当前会话。

-----------------------------------

文件描述符限制:

每个系统对文件描述符个数都有限制,配置ulimit也是为了调大系统的打开文件个数

服务器大并发的时候,如出现Socket/File: Can’t open so many files

-----------------------------

 epoll是poll的升级版

Poll本质上是Linux系统调用,其接口为int poll(struct pollfd *fds,nfds_t nfds, int timeout),作用是
监控资源是否可用。
举个例子,一个Web服务器建了多个socket连接,它需要知道里面哪些连接传输发了请求需要处理,功能与select系统
调用类似,不过poll不会清空文件描述符集合,因此检测大量socket时更加高效
×××××××××××××××××××××××

进程间通信:

IPC全称Interprocess Communication,指进程间协作的各种方法,当然包括共享内存,信号量或Socket等。
管道(Pipe)
管道是进程间通信最简单的方式,任何进程的标准输出都可以作为其他进程的输入。
信号(Signal)
下面马上会介绍。
消息队列(Message)
和传统消息队列类似,但是在内核实现的。
共享内存(Shared Memory)
不同进程间可以通过mmap()系统调用实现。
信号量(Semaphore)
信号量本质上是一个整型计数器,调用wait时计数减一,减到零开始阻塞进程,从而达到进程、线程间协作的作用。
套接口(Socket)
也就是通过网络来通信,这也是最通用的IPC,不要求进程在同一台服务器上。


调试与观察:

可以用"ps -ajx"命令观察一下你的daemon进程的状态和一些参数

可以把log重定向到文件里。__TIME__,__func__,__LINE__.是c里面比较有用的宏


附件1、用信号

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <errno.h>
 4 #include <stdlib.h>
 5 #include <signal.h>
 6 
 7 static void sig_child(int signo);
 8 
 9 int main()
10 {
11     pid_t pid;
12     //创建捕捉子进程退出信号
13     signal(SIGCHLD,sig_child);
14     pid = fork();
15     if (pid < 0)
16     {
17         perror("fork error:");
18         exit(1);
19     }
20     else if (pid == 0)
21     {
22         printf("I am child process,pid id %d.I am exiting.\n",getpid());
23         exit(0);
24     }
25     printf("I am father process.I will sleep two seconds\n");
26     //等待子进程先退出
27     sleep(2);
28     //输出进程信息
29     system("ps -o pid,ppid,state,tty,command");
30     printf("father process is exiting.\n");
31     return 0;
32 }
33 
34 static void sig_child(int signo)
35 {
36      pid_t        pid;
37      int        stat;
38      //处理僵尸进程
39      while ((pid = waitpid(-1, &stat, WNOHANG)) >0)
40             printf("child %d terminated.\n", pid);
41 }
附件2、fork两次

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <unistd.h>
 4 #include <errno.h>
 5 
 6 int main()
 7 {
 8     pid_t  pid;
 9     //创建第一个子进程
10     pid = fork();
11     if (pid < 0)
12     {
13         perror("fork error:");
14         exit(1);
15     }
16     //第一个子进程
17     else if (pid == 0)
18     {
19         //子进程再创建子进程
20         printf("I am the first child process.pid:%d\tppid:%d\n",getpid(),getppid());
21         pid = fork();
22         if (pid < 0)
23         {
24             perror("fork error:");
25             exit(1);
26         }
27         //第一个子进程退出
28         else if (pid >0)
29         {
30             printf("first procee is exited.\n");
31             exit(0);
32         }
33         //第二个子进程
34         //睡眠3s保证第一个子进程退出,这样第二个子进程的父亲就是init进程里
35         sleep(3);
36         printf("I am the second child process.pid: %d\tppid:%d\n",getpid(),getppid());
37         exit(0);
38     }
39     //父进程处理第一个子进程退出
40     if (waitpid(pid, NULL, 0) != pid)
41     {
42         perror("waitepid error:");
43         exit(1);
44     }
45     exit(0);
46     return 0;
47 }


附件3、disown的用法如下。

# 移出最近一个正在执行的后台任务
$ disown
# 移出所有正在执行的后台任务
$ disown -r
# 移出所有后台任务
$ disown -a
# 不移出后台任务,但是让它们不会收到SIGHUP信号
$ disown -h
# 根据jobId,移出指定的后台任务
$ disown %2
$ disown -h %2


附件4:以独立脚本的形式,使一个普通程序变成守护进程,用desktop文件指定进入桌面后启动,或者initrd等其他位置指定何时启动。

# -->/usr/share/applications/nemo-autostart.desktop

  1 [Desktop Entry]                                                                                                          
  2 Type=Application
  3 Name=Nemo
  4 Comment=Start Nemo desktop at log in
  5 Exec=/usr/bin/nemo-launcher
  6 OnlyShowIn=X-Cinnamon;
  7 AutostartCondition=GSettings org.nemo.desktop show-desktop-icons
  8 NoDisplay=true
                       

#-->/usr/bin/nemo-launcher  

  1 #! /usr/bin/python
  2 # -*- coding=utf-8 -*-
  3 
  4 import os, sys,time
  5 
  6 def check_pid_running():
  7     os.system("killall nemo > /dev/null 2>&1")
  8     cmd = "ps -ef | grep nemo-launcher | grep -v %s | awk '{print $2}' | xargs kill -9 > /dev/null 2>&1" % os.getpid()
  9     os.system(cmd)
 10     return 
 11 
 12 if __name__ == "__main__":
 13 
 14     check_pid_running();
 15   
 16     First_flag = 1                                                                                                       
 17     while True:
 18         time.sleep(1)
 19         cinnamon_pid = os.fork()
 20         if cinnamon_pid == -1:
 21             break;
 22         if cinnamon_pid == 0:
 23             ret = os.execvp("nemo", ("nemo", "-n", ) + tuple(sys.argv[1:]))
 24             if ret < 0:
 25                 continue
 26             sys.exit(0)
 27         if cinnamon_pid > 0 :
 28             cinnamon_pid = os.wait()[0]
 29 

附件5:正规的daemon写法

/* daemon.c */
#include<unistd.h>
#include<sys/types.h>
#include <sys/stat.h>
#define MAXFILE 65535
main()
{
	pid_t pid;
	int i;
        pid=fork();
	if(pid<0){
		printf("error in fork\n");
		exit(1);
	}else if(pid>0) 
		/* 父进程退出 */
		exit(0); 
	/* 调用setsid */
        setsid();
	/* 切换当前目录 */
        chdir("/");
	/* 设置文件权限掩码 */
	umask(0);
	/* 关闭所有可能打开的不需要的文件 */
	for(i=0;i<MAXFILE;i++)
		close(i);
	/* 
	   到现在为止,进程已经成为一个完全的daemon进程,
	   你可以在这里添加任何你要daemon做的事情,如:
	*/ 
	for(;;)
		sleep(10);
}

https://www.ibm.com/developerworks/cn/linux/kernel/syscall/part4/



参考文章:http://www.cnblogs.com/Anker/p/3271773.html

http://www.ruanyifeng.com/blog/2016/02/linux-daemon.html

https://linux.cn/article-7170-1.html#3_7862

http://www.cnblogs.com/mickole/p/3188321.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值