- 浏览: 799093 次
- 性别:
- 来自: 西安
文章分类
- 全部博客 (307)
- struts (8)
- hibernate (3)
- spring (32)
- opensourceproject (12)
- javaScript (9)
- primeton EOS (2)
- journey of heart (10)
- Design pattern (6)
- ejb (17)
- point (37)
- Linux&Unix (22)
- ibatis (10)
- AJAX (6)
- DB (26)
- Protocol (6)
- chart (4)
- web server (11)
- webservice (7)
- integration (3)
- tuxedo (5)
- ext (4)
- android (1)
- c/c++ (12)
- JVM (1)
- paginationFrame (2)
- code (2)
- report (1)
- High-performance web (1)
- svn (1)
- JQuery (1)
- workDaily (2)
- cloud (16)
- Python (8)
- English (2)
- shell (5)
- googleCode (1)
- nio (1)
- hyper-v (1)
- debug (3)
- vbs (2)
- openstack (3)
- K8S (1)
- Mesos (0)
- Spark (0)
- Marathon (0)
最新评论
-
钱图大展:
chao2751021 写道lib包哪里去下载,找不到
大型网站用户行为记录的一个实现--基于clickStream(第一部分) -
钱图大展:
无法下载
大型网站用户行为记录的一个实现--基于clickStream(第一部分) -
fm395728572:
shell脚本中用到了环境变量,但是获取不到,例如脚本中有一句 ...
ganymed-ssh2 for Java -
liuhanjiang:
我qq147229234
大型网站用户行为记录的一个实现--基于clickStream(第一部分) -
liuhanjiang:
博主 我利用您提供的方法实现博文中介绍的clickstream ...
大型网站用户行为记录的一个实现--基于clickStream(第一部分)
守护进程概述
守护进程,也就是通常所说的 Daemon 进程,是 Linux 中的后台服务进程。它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程常常在系统引导载入时启动,在系统关闭时终止。 Linux 有很多系统服务,大多数服务都是通过守护进程实现的,如本书在第二章中讲到的多种系统服务都是守护进程。同时,守护进程还能完成许多系统任务,例如,作业规划进程 crond 、打印进程 lqd 等(这里的结尾字母 d 就是 Daemon 的意思)。
由于在 Linux 中,每一个系统与用户进 行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关 闭。但是守护进程却能够突破这种限制,它从被执行开始运转,直到整个系统关闭时才会退出。如果想让某个进程不因为用户、终端或者其他的变化而受到影响,那 么就必须把这个进程变成一个守护进程。可见,守护进程是非常重要的。
编写守护进程
编写守护进程看似复杂,但实际上也是遵循一个特定的流程。只要将此流程掌握了,就能很方便地编写出用户自己的守护进程。下面就分 4 个步骤来讲解怎样创建一个简单的守护进程。在讲解的同时,会配合介绍与创建守护进程相关的几个系统函数,希望读者能很好地掌握。
1 .创建子进程,父进程退出
这是编写守护进程的第一步。由于守护进程是脱离控制终端的,因此,完成第一步后就会在 shell 终端里造成一种程序已经运行完毕的假象。之后的所有工作都在子进程中完成,而用户在 shell 终端里则可以执行其他的命令,从而在形式上做到了与控制终端的脱离。
到这里,有心的读者可能会问,父进程创建了子进程之后退出,此时该子进程不就没有父进程了吗?守护进程中确实会出现这么一个有趣的现象,由于父进程已经先于子进程退出,会造成子进程没有父进程,从而变成一个孤儿进程。在 Linux 中,每当系统发现一个孤儿进程,就会自动由 1 号进程(也就是 init 进程)收养它,这样,原先的子进程就会变成 init 进程的子进程了。其关键代码如下所示:
pid = fork();
if (pid > 0)
{
exit(0); /* 父进程退出 */
}
2 .在子进程中创建新会话
这个步骤是创建守护进程中最重要的一步,虽然它的实现非常简单,但它的意义却非常重大。在这里使用的是系统函数 setsid() ,在具体介绍 setsid() 之前,读者首先要了解两个概念:进程组和会话期。
进程组。
进程组是一个或多个进程的集合。进程组由进程组 ID 来惟一标识。除了进程号( PID )之外,进程组 ID 也是一个进程的必备属性。
每个进程组都有一个组长进程,其组长进程的进程号等于进程组 ID 。且该进程 ID 不会因组长进程 的退出而受到影响。
会话期
( 1 ) setsid() 函数作用。
setsid() 函数用于创建一个新的会话,并担任该会话组的组长。调用 setsid() 有下面的 3 个作用。
- 让进程摆脱原会话的控制。
- 让进程摆脱原进程组的控制。
- 让进程摆脱原控制终端的控制。
那么,在创建守护进程时为什么要调用 setsid() 函数呢?读者可以回忆一下创建守护进程的第一步,在那里调用了 fork() 函数来创建子进程再令父进程退出。由于在调用 fork() 函数时,子进程全盘复制了父进程的会话期、进程组和控制终端等,虽然父进程退出了,但原先的会话期、进程组和控制终端等并没有改变,因此,还不是真正意义上的独立,而 setsid() 函数能够使进程完全独立出来,从而脱离所有其他进程的控制。
( 2 ) setsid() 函数格式 。
3 .改变当前目录为根目录
这一步也是必要的步骤。使用 fork() 创建的子进程继承了父进程的当前工作目录。由于在进程运行过程中,当前目录所在的文件系统(比如“ /mnt/usb ”等)是不能卸载的,这对以后的使用会造成诸多的麻烦(比如系统由于某种原因要进入单用户模式)。因此,通常的做法是让“ / ”作为守护进程的当前工作目录,这样就可以避免上述的问题,当然,如有特殊需要,也可以把当前工作目录换成其他的路径,如 /tmp 。改变工作目录的常见函数是 chdir() 。
4 .重设文件权限掩码
文件权限掩码是指屏蔽掉文件权限中的对应位。比如,有一个文件权限掩码是 050 ,它就屏蔽了文件组拥有者的可读与可执行权限。由于使用 fork() 函数新建的子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带来了诸多的麻烦。因此,把文件权限掩码设置为 0 ,可以大大增强该守护进程的灵活性。设置文件权限掩码的函数是 umask() 。在这里,通常的使用方法为 umask(0) 。
5 .关闭文件描述符
同文件权限掩码一样,用 fork() 函数新建的子进程会从父进程那里继承一些已经打开了的文件。这些被打开的文件可能永远不会被守护进程读或写,但它们一样消耗系统资源,而且可能导致所在的文件系统无法被卸载。
在上面的第二步之后,守护进程已经与所属的控制终端失去了联系。因此从终端输入的字符不可能达到守护进程,守护进程中用常规方法(如 printf() )输出的字符也不可能在终端上显示出来。所以,文件描述符为 0 、 1 和 2 的 3 个文件(常说的输入、输出和报错这 3 个文件)已经失去了存在的价值,也应被关闭。通常按如下方式关闭文件描述符:
for(i = 0; i < MAXFILE; i++)
{
close(i);
}
一个标准 后台进程 daemon 的写法, 教科书式: daemon.c
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<fcntl.h> #include<sys/types.h> #include<unistd.h> #include<sys/wait.h> int main() { pid_t pid; int i, fd; char *buf = "This is a Daemon\n"; pid = fork(); /* 第一步 */ if (pid < 0) { printf("Error fork\n"); exit(1); } else if (pid > 0) { exit(0); /* 父进程推出 */ } setsid(); /*第二步*/ chdir("/"); /*第三步*/ umask(0); /*第四步*/ for(i = 0; i < getdtablesize(); i++) /*第五步*/ { close(i); } /*这时创建完守护进程,以下开始正式进入守护进程工作*/ while(1) { if ((fd = open("/tmp/daemon.log", O_CREAT|O_WRONLY|O_APPEND, 0600)) < 0) { printf("Open file error\n"); exit(1); } write(fd, buf, strlen(buf) + 1); close(fd); sleep(10); } exit(0); }
编译:
gcc -o daemon daemon.c
运行: ./daemon
$ tail -f /tmp/daemon.log
This is a Daemon
This is a Daemon
This is a Daemon
This is a Daemon
…
$ ps -ef|grep daemon
76 root 1272 S ./daemon
85 root 1520 S grep daemon
进程创建方式:
1. fork (see above sample)
2. exec ,6 个函数 分为 2个系列 C 系列 和L 系列
C 系列: execv , execve, execvp
L 系列 : execl , execle, execlp
L 是list(列表的意思), 表示函数需要将每个命令行的参数作为函数的参数进行传递
V是vector(矢量的意思),表示所有函数包装成一个矢量数组中传递
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> int main(int argc,char *argv[],char *env[]) { pid_t pid; char* para[]={"ls","-a",NULL}; if((pid=fork())<0){ perror("fork"); exit(0); } if(pid==0){ if(execl("/bin/ls","ls","-l",(char*)0)==-1){ perror("fork"); exit(0); } } if((pid=fork())<0){ perror("fork"); exit(0); } if(pid==0){ if(execv("/bin/ls",para)==-1){ perror("fork"); exit(0); } } return 0; }
3.system
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> int main(int argc,char *argv[],char *env[]) { printf("Call is return %d\n",system("ls -l")); }
进程的终止:
一个进程结束运行时,会向其父进程发送SIGCLD 信号, 父进程在接收到 SIGCLD 信号后可以忽略该信号或者安装信号处理函数处理该信号,通常情况下,父进程调用wait 函数 等待子进程退出,并获取子进程的返回码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <syspes.h> #include <sys/wait.h> #include <signal.h> void handle_sigcld(int signo){ pid_t pid; int status; if((pid=wait(&status))!=-1) { printf("child %d process exit\n",pid); } if(WIFEXITED(status)){ // 判断子进程退出时是否有返回码 printf("child process return code %d\n",WEXITSTATUS(status)); } if(WIFSIGNALED(status)) // 判断子进程是否被信号中断而结束 { printf("child process %d is interrupt by signal\n",WTERMSIG(status)); } } int main(int argc,char *argv[],char *env[]) { pid_t pid; signal(SIGCLD,handle_sigcld); if((pid=fork())<0) { perror("fork"); exit(0); } if(pid==0) { sleep(2); exit(123); } sleep(5); }
[root@egovmo03 C]# ./a.out
child 3586 process exit
child process return code 123
发表评论
-
static作用(修饰函数、局部变量、全局变量)
2016-06-05 15:45 1123在C语言中,static的字面意思很容易把我们导入歧途 ... -
typedef
2014-06-08 14:27 851用 typedef 定义类型 10 用 typedef ... -
linux C语言系列--第七讲--信号
2012-07-22 11:14 1338啦啦 -
libxml 解析xml
2012-07-18 21:49 0TODO -
c 函数的返回值
2012-07-16 06:10 1380无法把指针变量本身传递给一个函数 void Get ... -
如何编写有多个返回值的C语言函数
2012-02-21 22:25 0我们知道C语言函数的返回值是通过函数中的return语句来实现 ... -
c语言中的字符数组与字符串
2012-02-21 14:15 70361、字符数组的定义与初始化 字符数组的初始化,最容易理解 ... -
linux C语言系列--第五讲--函数
2011-06-22 08:09 1192可变参数列表: 例子:写一个函数,求给定所 ... -
全面复习linux C语言系列--第四讲--程序调试
2011-06-10 00:28 1353出错处理 • 1.使用标准错误输出 ... -
全面复习linux C语言系列--第三讲--make的使用
2011-06-09 07:42 1432make 概述 Make ... -
全面复习linux C语言系列--第二讲--GCC编译器
2011-06-08 07:49 16141.编译过程简述: 1. ... -
全面复习linux C语言系列--第一讲--基础知识
2011-06-07 23:27 1449看别人code的时候 ... -
Windows下用Eclipse搭建C/C++开发环境
2010-01-07 21:54 1151http://www.iteye.com/topic/5605 ...
相关推荐
第六章消息管理 …………………………………………………………………………39 第七章线程操作 …………………………………………………………………………49 第八章网络编程 …………………………………………...
第六章 消息管理 …………………………………………………………………………39 第七章 线程操作 …………………………………………………………………………49 第八章 网络编程 …………………………………………...
程序代码\chapter06 文件夹中包含了第6章的示例源程序。其中pointer1.c是6.2.2的第一个源代码,pointer2.c是6.2.2的第二个源代码,pointer3.c是6.2.2的第三个源代码,pointer4.c是6.2.3的第一个源代码,pointer5.c是...
1、每个进程用一个进程控制块PCB来代表,进程控制块包括进程名(进程的标识)、指针(把进程连成循环队列,用指针指出下一个进程的进程控制块首地址,最后一个进程中的指针指出第一个进程的进程控制块首地址)、已运行...
第6章 嵌入式linux c语言基础——数组、指针与结构 168 6.1 数组 169 6.1.1 一维数组 169 6.1.2 字符串 172 6.1.3 二维数组 174 6.2 指针 175 6.2.1 指针的概念 175 6.2.2 指针变量的操作 ...
共九个章节,第一章:基础知识 第二章:进程介绍 第三章:文件操作 第四章:时间概念 第五章:信号处理 第六章:消息管理 第七章:线程操作 第八章:网络编程 第九章:linux下C开发工具介绍 pdf文档
第6章 文件操作 第7章 进程控制 第8章 线程控制 第9章 信号及信号处理 第10章 进程间通信 第三篇 Linux网络和图形界面编程 第11章 网络编程 第12章 GTK+图形界面编程 第四篇 ...
第一章 基础知识 第二章 进程介绍 第三章 文件操作 第四章 时间概念 第五章 信号处理 第六章 消息管理 第七章 线程操作 第八章 网络编程 第九章 Linux 下 C 开发工具介绍
目 录 第一章 基础知识 第二章 进程介绍 第三章 文件操作 第四章 时间概念 第五章 信号处理 第六章 消息管理 第七章 线程操作 第八章 网络编程 第九章 Linux下 C 开发工具介绍
第6章 文件I/O编程 6.1 Linux系统调用及用户编程接口(API) 6.1.1 系统调用 6.1.2 用户编程接口(API) 6.1.3 系统命令 6.2 Linux中文件及文件描述符概述 6.3 底层文件I/O操作 6.3.1 基本文件操作 6.3.2 文件锁 6.3.3 ...
第六章 消息管理 …………………………………………………………………………39 第七章 线程操作 …………………………………………………………………………49 第八章 网络编程 …………………………………………...
5.1 嵌入式开发环境的搭建 5.2 U-Boot移植 5.3 实验内容——移植Linux内核 本章小结 思考与练习 第6章 文件I/O编程 6.1 Linux系统调用及用户编程接口(API) 6.2 Linux中文件及文件描述符概述 6.3 不...
第6章 传统的Unix进程间通信 6.1 概述 6.2 管道和系统调用pipe() 6.3 命名管道 6.4 信号 6. 5 系统调用ptrace()和进程跟踪 6.6 报文传递 6.7 共享内存 6.8 信号量 《LINUX内核源代码情景分析(下册)》图书...
第六章 消息管理 .................................................................................... 第七章 线程操作 ......................................................................................
第6章 传统的Unix进程间通信 6.1 概述 6.2 管道和系统调用pipe() 6.3 命名管道 6.4 信号 6. 5 系统调用ptrace()和进程跟踪 6.6 报文传递 6.7 共享内存 6.8 信号量 《LINUX内核源代码情景分析(下册)》图书...
第6章 进程间通信(IPC) 6.1 进程间通信机制概述 6.2 信号处理 6.3 管道 6.4 System V IPC机制 6.5 小结 习题 第7章 线程操作 7.1 线程概述 7.2 线程管理 7.3 小结 习题 第8章 网络编程 8.1 概述 8.2 ...
第6章 进程间通信(IPC) 6.1 进程间通信机制概述 6.2 信号处理 6.3 管道 6.4 System V IPC机制 6.5 小结 习题 第7章 线程操作 7.1 线程概述 7.2 线程管理 7.3 小结 习题 第8章 网络编程 8.1 概述 8.2 ...
第1篇 Linux下C语言基础 第1章 Linux简介 1.1 GNU简介 1.2 Linux简介 1.2.1 Linux发展史 1.2.2 Linux发行版 1.2.3 Linux内核版本 1.2.4 Linux与UNIX的关系 1.2.5 Linux在服务器方面的发展 1.2.6 Linux在嵌入式系统...
第6章 进程间通信(IPC) 6.1 进程间通信机制概述 6.2 信号处理 6.3 管道 6.4 System V IPC机制 6.5 小结 习题 第7章 线程操作 7.1 线程概述 7.2 线程管理 7.3 小结 习题 第8章 网络编程 8.1 概述 8.2 ...