守护进程
在Uinx中有所谓的守护进程(daemon),一种不会受电脑用户直接操纵的的后台执行的一种多任务操作系统上执行的电脑程序(通常以d命名结尾)。在Unix系统中直接位于1号进程init之下。此类进程无父进程可通过普通进程脱壳获得fork()。由于在Linux中,每一个系统与用户进行交流的界面称为终端,每一个从此终端开始运行的进程都会依附于这个终端,这个终端就称为这些进程的控制终端,当控制终端被关闭时,相应的进程都会自动关闭。但是守护进程却能够突破这种限制,它从被执行开始运转,直到整个系统关闭时才退出。如果想让某个进程不因为用户或终端或其他地变化而受到影响,那么就必须把这个进程变成一个守护进程。
守护进程实例子(RedHat Linux6.0)
守护进程实例包括两部分:主程序test.c和初始化程序init.c。主程序每隔一分钟向/tmp目录中的日志test.log报告运行状态。初始化程序中的init_daemon函数负责生成守护进程。读者可以利用init_daemon函数生成自己的守护进程。
1. init.c清单
#include < unistd.h >
#include < signal.h >
#include < sys/param.h >
#include < sys/types.h >
#include < sys/stat.h >
void init_daemon(void)
{
int pid;
int i;
if(pid=fork())
exit(0);//是父进程,结束父进程
else if(pid< 0)
exit(1);//fork失败,退出
//是第一子进程,后台继续执行
setsid();//第一子进程成为新的会话组长和进程组长
//并与控制终端分离
if(pid=fork())
exit(0);//是第一子进程,结束第一子进程
else if(pid< 0)
exit(1);//fork失败,退出
//是第二子进程,继续
//第二子进程不再是会话组长
for(i=0;i< NOFILE;++i)//关闭打开的文件描述符
close(i);
chdir("/tmp");//改变工作目录到/tmp
umask(0);//重设文件创建掩模
return;
}
2. test.c清单
#include < stdio.h >
#include < time.h >
void init_daemon(void);//守护进程初始化函数
main()
{
FILE *fp;
time_t t;
init_daemon();//初始化为Daemon
while(1)//每隔一分钟向test.log报告运行状态
{
sleep(60);//睡眠一分钟
if((fp=fopen("test.log","a")) >=0)
{
t=time(0);
fprintf(fp,"Im here at %s/n",asctime(localtime(&t)) );
fclose(fp);
}
}
}
编译:gcc -g -o test init.c test.c
执行:./test
查看进程:ps -ef
Java的守护线程
Java的语言机制是构建在jvm基础上。即Java平台把操作系统的底层屏蔽起来以便在自己的虚拟平台中构造对自己有利机制,这样的小平台相较于Unix系统显然不能完全以进程级别操作后台服务独立于Java控制终端,于是线程级别守护应运而生。
在Java的线程机制中有用户线程(user Thread)、以及守护线程(Deamon Thread)
守护线程为其他的用户线程的运行提供服务,但其实本质上没有太大区别,主要体现在周期相较于用户级的线程更长,是一种低级别线程,当所有的用户级线程都消失后,会自动离开。典型案例是jvm的系统资源回收线程。而当垃圾回收进程是最后一个线程时,虚拟机会自动离开。守护线程依赖于系统,与系统同生共死。
注意点:
Thread.setDaemon(true)必须在Thread之前设置否则会出现异常不能把正在运行的常规进程设置为守护进程。(ps与守护进程有明显区别:守护进程创建后会摆脱原会话的控制,摆脱进程组的控制,摆脱控制终端的控制,而守护线程则不行)
在Daemon线程中产生的线程一定也会是Daemon的(进程则不会)
不是所有的应用都可以分配给Daemon进程进行的,比如读写或者计算逻辑,因为在Daemon Thread还没来得及进行操作,虚拟机或许已经退出了(没有了用户线程)
守护线程实例
//文件是处的守护线程任务、
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
public class TextRunnable implements Runnable{
//完成文件输出的守护级线程任务
@Override
public void run() {
// TODO Auto-generated method stub
try{
Thread.sleep(1000);//守护线程阻塞1s后运行
File f = new File("E:\\daemon.txt");
FileOutputStream os = new FileOutputStream(f,true);
os.write("Ljj_fly".getBytes());//将字节流写入文件,Sting需要转换成Byte【】形式
}catch(IOException e){
e.printStackTrace();
}catch(InterruptedException e){
e.printStackTrace();
}
}
public static void main(String[] args)throws InterruptedException{
Thread s = new Thread(new TextRunnable());
//设置守护线程
s.setDaemon(true);
s.start();
}
}
此时发现没有创建daemon的文件,里面也没有字符串。
此时如果把设置守护县城的代码勾掉再次运行发现
java中一个线程的run方法只能执行一次,守护线程所能执行的操作与非守护线程没有本质的区别,也不一定要全程运行,也有可能一个守护线程只是在特定情况下才start,并且很快执行完run方法,然后被销毁。
错误思考:故此产生此线程的main线程结束了,守护线程因为还有其他用户线程的原因没有结束,还在就绪状态等待,因为相当低的优先级。
正确理解:JRE判断程序是否执行结束的标准是所有的前台执线程行完毕了,而不管后台线程的状态,即这个后台线程因为前台线程关闭太早导致了程序run()方法肯还没有执行或者执行一般就结束了。