防止产生僵尸进程的方法:
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