目录
一、Linux的守护进程(Daemon Process)
参考书籍:《UNIX环境高级编程(第二版)》
1.守护进程简介
守护进程也称精灵进程(Daemon Process)是一类在后台运行的特殊进程,生存期较长、用于执行系统特定事务活动的一种进程。很多守护进程在系统引导的时候启动,仅在系统关闭时才终止。另一些只在需要的时候才启动,完成任务后就自动结束,一般不需要用户交互。(例如:mysqld、httpd、udevd等)
2.了解Linux系统下的一些进程
通过命令ps -auj或者ps -efjc可以查看到以下内容:(截图有删减)
(1)内核进程
父进程号(PPID)为0的进程通常是内核进程,内核进程依赖于操作系统实现,作为系统自举过程的一部分而启动。(init进程是内核进程的例外,它是内核在自举启动的用户层命令)。内核进程是特殊的,通常存在系统的整个生命周期中,以超级用户特权运行,无控制终端,无命令行。
(2)init进程
进程号(PID)为1的进程,是内核启动的第一个用户级进程,是所有用户进程的祖先进程,也是所有孤儿进程的父进程(init会领养孤儿进程)。早期UNIX版本是/etc/init,后期较新版本是/sbin/init。init也是一个系统守护进程,负责个运行层次特定的系统服务,这些服务通常是在其本身拥有的守护进程帮助下实现的。
(3)守护进程🔖
🔖大多数守护进程都以超级用户(用户ID为0)特权运行
(PS:也就是UID为root)
🔖所有守护进程不依赖控制终端,其终端名设置为问号(TTY为?)
(PS:与终端无关且运行于后台,终端退出不会导致守护进程退出)
🔖多数守护进程的父进程是init进程
🔖生存周期长,一般从操作系统启动到关闭
(详见《UNIX环境高级编程(第二版)》第13章、第8.2节)
(4)调度进程
PID为0的进程通常是调度进程,常常被称作交换进程(swapper),调度进程是内核的一部分,并不执行任何磁盘上的程序,因此也被称为系统进程。
3.一个记录时间的守护进程Demo1.c
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
static bool flag = true;
int fd;
void handler(int sigNum){
printf("Daemon Process without terminal\n"); //不会打印到终端
write(fd,"Daemon Process receive SIGQUIT\n",32); //守护进程收到SIGQUIT信号结束并关闭文件
close(fd);
flag = false;
}
int main(){
int cnt;
time_t t;
char *buffer = (char *)malloc(64);
/* 利用daemon函数创建守护进程 */
if(daemon(0,0) == -1){
perror("daemon");
return -1;
}
/* 打开daemon.log文件 O_APPEND每次写之前,都将文件指针移动到文件的末端 */
/* 且能保证文件操作是原子的,不需要加锁 */
fd = open("/home/socket/Desktop/daemon.log",O_CREAT | O_WRONLY | O_APPEND,0644);
if(fd == -1)perror("open");
/* 信号处理:收到SIGQUIT信号调用处理函数 */
struct sigaction act;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if(sigaction(SIGQUIT,&act,NULL) == -1){
printf("sigaction error");
return -1;
}
/* 未收到SIGQUIT,每10s记录当前时间,写入daemon.log文件*/
while(flag){
t = time(0);
buffer = asctime(localtime(&t));
cnt = write(fd,buffer,strlen(buffer));
memset(buffer,'\0',cnt);
sleep(10);
}
return 0;
}
🔖注意:这里创建的守护进程还无法实现开机自启,如果需要该守护进程开机自启,需要通过/etc/rc.local这个文件,这是Linux系统启动后,会自动执行的一个配置脚本,但一般默认没有执行权限。(rc是run command的意思)。
(该文件默认无执行权限)
4.如何设置守护进程开机自启
(1)修改/etc/rc.local文件权限
(2)在rc.local添加守护进程执行程序的绝对路径
(注意需要程序的绝对路径,这样就可以实现守护进程的开机自启)
5.利用守护进程实现程序异常重启
(1)如何判断程序是否在运行Demo2.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
/* 判断a.out的程序是否在运行 */
int isRunning(){
FILE *file;
char *cmd = "ps -elf | grep a.out |grep -v grep"; //运行命令
char *buffer = (char *)malloc(128); //存放popen的执行输出
file = popen(cmd,"r"); //运行cmd,将结果写到file文件
fgets(buffer,128,file); //读取file文件内容到缓存buffer中
/* 查找buffer里是否有a.out的相关字符 */
if(strstr(buffer,"a.out") != NULL){
return 1;
}else{
return 0;
}
}
int main(){
if(isRunning() == 1){
printf("a.out is running now\n");
}else{
printf("a.out is not running\n");
}
return 0;
}
(2)守护进程实现程序异常自动重启Demo3.c
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include <signal.h>
#include <fcntl.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
static bool flag = true;
int fd;
/* 守护进程收到信号则结束守护a.out */
void handler(int sigNum){
flag = false;
}
/* 判断a.out的程序是否在运行 */
int isRunning(){
FILE *file;
char *cmd = "ps -elf | grep a.out |grep -v grep"; //运行命令
char *buffer = (char *)malloc(128); //存放popen的执行输出
file = popen(cmd,"r"); //运行cmd,将结果写到file文件
fgets(buffer,128,file); //读取file文件内容到缓存buffer中
/* 查找buffer里是否有a.out的相关字符 */
if(strstr(buffer,"a.out") != NULL){
return 1;
}else{
return -1;
}
}
int main(){
int cnt;
time_t t;
char *buffer = (char *)malloc(64);
/* 利用daemon函数创建守护进程 */
if(daemon(0,0) == -1){
perror("daemon");
return -1;
}
/* 信号处理:收到SIGQUIT信号调用处理函数 */
struct sigaction act;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if(sigaction(SIGQUIT,&act,NULL) == -1){
printf("sigaction error");
return -1;
}
/* 未收到SIGQUIT,每2s守护a.out。
判断程序运行状态,退出则重新启动 */
while(flag){
if(isRunning() != 1){
/* a.out绝对路径,&后台运行 */
system("/home/socket/Desktop/a.out &");
}
sleep(2);
}
return 0;
}
(最后只需要将守护进程设置开机自启,就可以实现对a.out程序的“守护”)
二、Linux的设备管理器udev(暂略)
1.udev概述
2.udev机制实现自动挂载u盘
说明:由于笔者水平有限,文中难以避免有所错漏,敬请各读者斧正
版权声明:转载请附上原文出处链接及本声明。