Linux信号处理和守护进程
2016-11-25 18:59
399 查看
一、信号的概念 - 信号随时发生,接受信号的进程也可以没有控制权 - 每个信号名都以SIG开头,包含在<signal.h>中 - 当进程接收到一个信号,他可以对信号采取如下操作之一: 忽略这个信号 捕获这个信号,这需要执行一段称为信号处理器的特殊代码 允许执行信号的默认操作 - 当进程对发送给它的信号采取措施,叫信号被传送 - 产生信号和递送信号之间的时间间隔叫做信号为决 - 进程不能简单的通过判断一个变量,如errno来判断是否出现一个信号 二、捕获信号 - 当一个进程调用fork的时候,其子进程继承父进程的信号处理方式 所以信号捕捉函数的地址在子进程中式有意义的 - 利用函数指针回调函数捕获信号 - 进程捕捉到信号并对信号进行处理时,进程正在执行的指令序列临 时中断,它首先执行该信号处理程序中的指令 - 如果信号处理程序返回(没有调用exit(0)或者abort),则继续执行在 捕捉到信号时正在执行的正常指令序列 - 在信号处理程序中,不能判断捕捉到信号时进程正在何处执行 signal 函数(若为命令行,则使用kill向指定进程发送信号) #include <signal.h> void (*signal(int signo,void (*func)(int)))(int); //参数signo是前面表格中的信号名 //参数func式接收到此信号后要调用的函数。该函数有个int 型参数,int代表捕获到的信号值 /*signal函数的例子*/ void catch_Signal(int Sign) { switch (Sign) { case SIGINT: printf ("SIGINT Signal\n"); break;//改变了消息的用途,可以收到CTRL+C后退出进程 case SIGALRM: printf ("HELLO\n"); exit(0); } } int main() { signal(SIGINT,catch_Signal); signal(SIGALRM,catch_Signal); pause (); return 0; } 三、发送信号 1)kill - 使用kill命令 - 使用kill函数 #include <signal.h> #include <sys/types.h> int kill(pid_t pid,int sig) //参数pid指定一个要杀死的信号,而sig是要发送的信号 2)raise函数 #include <signal.h> int raise(int signo); //kill 函数将信号发送给进程,raise函数允许发信号给自身 //raise(signo)等价于kill(getpid(),signo); 3)alarm函数 - alarm函数设置一个定时器,当定时器到了就发送SIGALRM信号 #include <unistd.h> unsigned int alarm(unsigned int seconds); //seconds是计时器时间到后时钟的秒数 //如果没有设置其他超时,函数返回0,否则返回值为前面安排超时中保留的秒数 //一个进程只能设置一次超时 //把second设置为0可以取消前面的超时设置 四、改进捕获信号机制 sigaction函数的功能是检查或修改与指定信号相关联的 处理动作,该函数替代了signal函数 #include <studio.h> int sigaction (int signo,const struct sigaction *act, struct sigaction *oact); //参数signo是要检测或者修改其具体动作的信号编号 (或同时执行这两种操作) //如果act指针为空,则要修改其动作 //如果oact指针为空,则系统由oact指针返回该信号的上一个动作 //成功返回0,失败返回-1 struct sigaction{ void (*sa_handle)(int); void (*sasigaction)(int,siginfo_t *,void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer )(void); } /*sigaction例子*/ void catch_Signal(int Sign) { switch (Sign) { case SIGINT: printf ("SIGINT Signal\n"); exit(0); } } int signal1(int signo,void (*func)(int)) { struct sigaction act,oact; act.sa_handler = func;//回调函数初始化 sigemptyset(&act.sa_mask);//初始化 act.sa_flags = 0; return sigaction(signo,&act,&oact); } int main() { signal1(SIGINT,catch_Signal); pause(); return 0; } 五、使用自定义信号 /*自定义signal函数的例子*/ int static status = 0; void catch_Signal(int Sign) { switch (Sign) { case SIGINT: printf ("SIGINT Signal\n"); break; case SIGUSR1: status = 1; } } int signal1(int signo,void (*func)(int)) { struct sigaction act,oact; act.sa_handler = func;//回调函数初始化 sigemptyset(&act.sa_mask);//初始化 act.sa_flags = 0; return sigaction(signo,&act,&oact); } /*捕获SIGUSR1信号*/ int main() { signal(SIGINT,catch_Signal); signal(SIGUSR1,catch_Signal); printf("pid = %d\n",getpid()); while(1) { if(status = 1); printf("SIGUSR1 UP!\n"); sleep(1); } return 0; } /*发送signal信号*/ int main(int argc,char *args[]) { if(argc > 1) { kill(atoi(args[1],SIGUSR1); printf("sent to %d\n",atoi(args[1])) } return 0; } 六、守护进程(daemon) - 守护进程是一个后台进程,无须用户输入就能运行 - 守护进程不能够控制终端,所以任何输入或者输出都需做特殊处理 - 守护进程执行fork之后就让父进程退出 - 子进程中调用setsid,取消进程和任何终端的关联 - 下一步是让根目录成为进程的当前工作目录(因为如果它的当前目录是在一个被安装的 文件系统上,那么就会妨碍这个文件系统被卸载) - 接下来设置umask为0(为了避免守护进程集成的umask收到创建文件和目录操作的干扰,这 一步是必要的) - 最后关闭子进程继承的任何不必要的文件描述符 总结: 1)父进程中执行fork,执行exit退出 2)在子进程中调用setid 3)让根目录'/'成为子进程的工作目录 4)把子进程的umask变为0 5)关闭不需要的文件描述符 setsid函数:pid_t setsid() //setsid函数创建一个新会话和一个新进程组,然后守护进程成为新会话的领导 //以及新进程组的领导 //setsid还保证新会话没有控制终端 //如果调用进程已经是一个进程组的领导进程,setsid会失败 //setsid调用成功返回新会话id,失败返回-1,并设置errno chdir函数:int chdir(const char*pathname) //chdir函数根据参数pathname设置当前工作目录 //chdir函数调用成功返回0,失败返回-1,并设置errno umask函数:mode_t umask(mode_t mask) //umask调用把守护今晨的umask设置为0,这样取消了父进程的umask,避免了潜在的 //干扰创建文件和目录 /*创建守护进程代码*/ int main() { pid_t pid = fork(); if (pid == -1) { return -1; } if (pid > 0) { exit(0); } if(pid == 0) { setsid();//脱离控制台 chdir("/");//非必须,防止mount出错 umask(0); close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); } while(1) { printf("hello\n"); sleep(1); } return EXIT_SUCCESS; } 守护进程写入系统日志: 1)openlog函数打开日志:void openlog(const char *ident,int option,int facility); //参数ident是要向每个消息加入的字符串,典型的情况是程序名称 //参数option是下面一个或多个值的"或" 名称 含义 LOG_CONS 如果系统日志服务器不能用, 写入控制台 LOG_NDELAY 立即打开连接,正常情况下,直到发送第一条消息才打开连接 LOG_PERROR 打印输出到stderr LOG_PID 每条消息中包含进程PID //参数facitity指定程序发送消息的类型 名称 含义 LOG_AUTHPRIV 安全授权信息 LOG_CRON 时钟守护进程,cron和at LOG_DAEMON 其他系统守护进程 LOG_KERN 内核消息 LOG_LPR 打印机子系统 LOG_MAIL 邮件子系统 LOG_USER 默认 2)syslog写入日志:void syslog (int priority,const char * format,...) //参数priority指定消息重要性 名称 含义 LOG_EMERG 系统不能使用 LOG_ALERT 立即采取措施 LOG_CRIT 紧急事件 LOG_ERR 出错条件 LOG_WARNING 警告条件 LOG_NOTICE 正常但重大事件 LOG_INFO 信息消息 LOG_DEBUG 调试信息 3)closelog关闭日志:void closelog(void); /*syslog日志例子*/ syslog(LOG_INFO,"my daemon is OK!"); /*严格说,openlog和closelog是可选的,因为函数syslog在首次使用的时候自动打开*/ 使用信号与守护进程通信 void catch_Signal(int Sign) { switch(Sign) { case SIGTERM: exit(EXIT_SUCCESS); } } /*通过脚本结束守护程序*/ #!/bin/sh WHOAMI=`whoami` PID=`ps -u $WHOAMI | grep mydaemon | awk '{print} $1'` if(test "$PID"!="") then kill $PID fi /*通过脚本开启守护程序*/ #!/bin/sh WHOAMI=`whoami` PID=`ps -u $WHOAMI | grep abc | awk '{print} $1'` if(test "$PID"="") then ./mydaemon fi
相关文章推荐
- Linux 守护进程出错处理
- linux多进程——进程组与会话、守护进程、信号通信 .
- Linux守护进程加上发送信号固定模式
- linux下的僵尸进程处理SIGCHLD信号
- Linux下信号通信实现A进程死循环输出A后被C进程处理输出C
- linux多进程——进程组与会话、守护进程、信号通信
- linux进程管理之信号处理
- C语言实现简单的守护进程及信号处理
- Linux下信号SIGCHLD处理不当产生僵尸进程的问题
- linux中的信号2——进程如何处理信号?
- Linux 守护进程出错处理
- 【原创】《Linux高级程序设计》杨宗德著 - 进程管理与程序开发 - Linux常见信号及处理 分类: Linux --- 应用程序设计 2014-11-08 11:54 68人阅读 评论(0) 收藏
- linux进程管理之信号处理(1)
- linux下的僵尸进程处理SIGCHLD信号
- linux信号处理、killall、SIGALRM、sigaction函数和结构体、向进程发送信号
- Linux 守护进程出错处理
- 说说Linux中的信号处理和僵尸进程的避免
- linux下的僵尸进程处理SIGCHLD信号
- linux下的僵尸进程处理SIGCHLD信号
- [转] linux下的僵尸进程处理SIGCHLD信号