精华内容
下载资源
问答
  • windows服务守护进程

    千次阅读 2020-04-29 13:55:29
    windows服务守护进程为什么选择用服务来守护进程注册服务双服务互相守护服务启动进程并保活启动进程进程保活 最近做一个项目, 保护我们云电脑中的计费程序等应用程序、脚本,不被用户结束。 综合各种方案,最终选择...


    去年做一个项目,
    保护我们云电脑中的计费程序等后台程序、脚本,不被用户结束。
    综合各种方案,最终选择了双服务互相守护,服务1监管我们的计费程序不被用户结束。

    为什么选择用服务来守护进程

    1. 大多数Windows服务是以SYSTEM用户启动的;
    2. 拥有最高的权限,例如:无需UAC就能以管理员权限启动一个进程;
    3. 可以操作注册表中Local Machine、系统目录;
    4. 服务可以先于 用户登录 启动,用户注销时服务也不会停止;
    5. 服务的隐蔽性更高,服务的自启动大部分人不了解(大部分用户想要关闭程序的自启动,首先会从开始菜单、启动项、计划任务、注册表等位置进行检查,很少有人会想到服务自启动);

    注册服务

    程序启动,构建一个迷惑性强的路径和文件名,例如:"C:\Windows\System32\SystemConfig.exe
    拷贝自身到指定路径,覆盖旧版本文件

    执行命令行操作 创建服务 设置自启动
    (替换代码中的ServiceName为你的服务名,ServicePath为服务程序所在路径)

    system("sc create ServiceName binPath= ServicePath start= auto");
    

    启动服务
    (替换代码中的ServiceName为你的服务名)

    system("sc start ServiceName");
    

    添加服务描述
    (替换代码中的ServiceName为你的服务名)

    system("sc description ServiceName\"这里描述你的服务做什么事. If this service is stopped or disabled, the system may not work properly. \"");
    

    配置服务
    (替换代码中的ServiceName为你的服务名)

    system("sc config ServiceName type= interact type= own");
    

    interact - 指定可以与桌面进行交互的服务,以接收用户的输入,
    交互式服务必须在LocalSystem帐户下运行,
    此类型必须与type = own或type = shared结合使用(例如,type = interact type = own),
    单独使用type = interact将产生错误。

    own - 指定在其自己的进程中运行的服务,
    它不与其他服务共享可执行文件,这是默认值。

    调用StartServiceCtrlDispatcher将服务进程的主线程连接到服务控制管理器,使该线程成为服务控制调度程序的线程
    (替换代码中的ServiceName为你的服务名,ServiceMain为你的服务主函数)
    ServiceTable 表中最后一个条目的成员必须为NULL值,才能指定表的末尾。

    int main(int argc, char const* argv[]) {
    
        RegisterService(); /* 注册服务 */
    
        SERVICE_TABLE_ENTRY ServiceTable[] = {
            { ServiceName1, ServiceMain1 },
            { ServiceName2, ServiceMain2 },
            { NULL, NULL },
        };
        return StartServiceCtrlDispatcher(ServiceTable);
    }
    

    双服务互相守护

    在这里插入图片描述

    在服务主函数(ServiceMain)中 注册 服务控制处理器(CtrlHandler)
    在 服务控制处理器CtrlHandler 中对 服务停止消息 与 关机消息进行处理
    服务1在被停止时,设置互斥体 MutexService1 为有信号,服务2接受到信号,启动服务1
    服务1在被停止时,设置互斥体 MutexService2 为有信号,服务1接受到信号,启动服务2

    //服务主函数
    void WINAPI ServiceMain(DWORD argc, LPTSTR* argv) {
    
        ServiceStatus.dwServiceType = SERVICE_WIN32;//该服务在其自己的进程中运行|该服务与其他服务共享进程
        ServiceStatus.dwCurrentState = SERVICE_START_PENDING;//初始化状态
        ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;//系统关机时通知服务 | 该服务可以停止
        ServiceStatus.dwWin32ExitCode = NO_ERROR;
        ServiceStatus.dwServiceSpecificExitCode = NO_ERROR;
        ServiceStatus.dwCheckPoint = 0;
        ServiceStatus.dwWaitHint = 0;
    
        hStatus = RegisterServiceCtrlHandler(ServiceName, CtrlHandler);//服务注册控制处理器
        if (!hStatus) {
            DWORD dwError = GetLastError();
            WriteValueAndMsgToLog(dwError, "注册服务控制处理器");
            return;
        }
    
        InitService();//初始化服务
        
        //设置服务状态
        ServiceStatus.dwCurrentState = SERVICE_RUNNING;
        SetServiceStatus(hStatus, &ServiceStatus);
    
        Run();//服务的功能部分
    }
    
    //服务1的控制处理器
    void WINAPI CtrlHandler1(DWORD fdwControl) {
    
        switch (fdwControl) {
        case SERVICE_CONTROL_STOP: {
            WriteMsgToLog("Service1 服务停止...");
            ServiceStatus1.dwCurrentState = SERVICE_STOPPED;
            ServiceStatus1.dwWin32ExitCode = 0;
            SetServiceStatus(hStatus1, &ServiceStatus1);
    
            HANDLE hMutexService1 = OpenMutex(MUTEX_ALL_ACCESS, FALSE, "MutexService1");//获取互斥体MutexService1的句柄
            ReleaseMutex(hMutexService1);//设置互斥体MutexService1为有信号
            CloseHandle(hMutexService1);
    
            break;
        }
        case SERVICE_CONTROL_SHUTDOWN: {
            WriteMsgToLog("Service1 服务终止...");
            ServiceStatus1.dwCurrentState = SERVICE_STOPPED;
            ServiceStatus1.dwWin32ExitCode = 0;
            SetServiceStatus(hStatus1, &ServiceStatus1);
            break;
        }
        default:
            break;
        }
    }
    
    //服务2的功能部分
    void Run() {
        HANDLE hMutexService2 = CreateMutex(NULL, TRUE, "MutexService2");//创建互斥体MutexService2 刚被创建处于有信号状态 可被等待
        WaitForSingleObject(hMutexService2, INFINITE);//将互斥体设为无信号
    
        HANDLE hMutexService1 = NULL;
        while (true) {										   //MutexService1
            hMutexService1 = OpenMutex(MUTEX_ALL_ACCESS, FALSE, "MutexService1");//获取互斥体MutexService1的句柄
            if (hMutexService1 != NULL) {
                WaitForSingleObject(hMutexService1, INFINITE);//将互斥体MutexService1设为无信号
                system("sc start ServiceName1");
            }
        }
    }
    

    服务启动进程并保活

    在windows中,每一个用户会有一个Session ,Session 0专用于服务和其他不与用户交互的应用程序;
    第一个登录,可以进行交互式操作的用户被连到Session 1上,第二个登录进行的用户被分配给Session 2,…;
    因为服务在Session 0中,而我们登陆系统以后看到的桌面属于另一个Session;
    服务没有权限在桌面启动程序;

    下面我们就来介绍,绕过限制的方法:
    1.简单的做个快照,遍历进程,检查桌面(explorer.exe)是否启动;或者用WMI去监控桌面启动;
    2.提权SE_TCB_NAME

    void RaiseToTCB_NAME()
    {
        HANDLE hToken;
        HANDLE hProcess = GetCurrentProcess();  // 获取当前进程句柄
    
        // 打开当前进程的Token,就是一个权限令牌,第二个参数可以用TOKEN_ALL_ACCESS
        if (OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
        {
            TOKEN_PRIVILEGES tkp;
            if (LookupPrivilegeValue(NULL, SE_TCB_NAME, &tkp.Privileges[0].Luid))
            {
                tkp.PrivilegeCount = 1;
                tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    
                //通知系统修改进程权限
                BOOL bREt = AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0);
                //WriteErrorToLog(bREt, "获取TCB_NAME权限");
            }
            CloseHandle(hToken);
        }
    }
    

    3.调用 WTSGetActiveConsoleSessionId 获取 控制台会话的会话标识符
    调用 WTSQueryUserToken 获取会话ID指定的登录用户的主要访问令牌,调用此函数,调用应用程序必须在LocalSystem帐户的上下文中运行,并且具有 SE_TCB_NAME特权。

    HANDLE hToken = NULL;
    WTSQueryUserToken(WTSGetActiveConsoleSessionId(), &hToken);
    

    启动进程

    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    ZeroMemory(&si, sizeof(STARTUPINFO));
    ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
    si.cb = sizeof(STARTUPINFO);
    char cDesktop[MAX_PATH] = "winsta0\\default";
    si.lpDesktop = cDesktop;
    si.wShowWindow = SW_SHOW;
    si.dwFlags = STARTF_USESHOWWINDOW;
    
    LPVOID pEnv = NULL;
    DWORD dwCreationFlag = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_UNICODE_ENVIRONMENT;
    CreateEnvironmentBlock(&pEnv, hToken, FALSE);
    CreateProcessAsUser(hToken, NULL, szExePath, NULL, NULL, FALSE, dwCreationFlag, pEnv, NULL, &si, &pi);
    

    进程保活

    等待进程退出后,再次拉起

    WaitForSingleObject(pi.hProcess, INFINITE);
    
    展开全文
  • windows创建守护进程的简便方式

    千次阅读 2012-04-05 08:38:04
    这是一篇关于Win下创建守护进程的文章,很入门,为了NAE的邀请码! 前段时间做了一个代理工具,主要功能是将线上assets服务器(单独域名存放css、js或其它文件)指向本地80端口,当请求文件在磁盘上存在则读取,...

    这是一篇关于Win下创建守护进程的文章,很入门,为了NAE的邀请码!

    前段时间做了一个代理工具,主要功能是将线上assets服务器(单独域名存放css、js或其它文件)指向本地80端口,当请求文件在磁盘上存在则读取,否则用http.get方式获取线上内容。

    由于工具运行前需要修改hosts文件,因此想让这一过程自动化。即,代理启动时,自动添加hosts绑定,反之则去除绑定。

    最开始,我必须开启两个不同的CMD窗口,来分别启动2个程序,否则,在一个CMD窗口用node进程模块启动的进程,当用户关闭CMD窗口时,子进程也随之退出了。

    因此,必须确保守护进程脱离终端控制,这种程序在不同系统中可能会被称为服务、后台进程、僵尸程序,或者守护进程等等。此应用场景中,应该称为后台进程,但为了好理解一点,仍称为守护进程。

    守护进程至少有2个特点:

    • 尽可能一直运行
    • 本身足够健壮

    特点1很好理解,这样才能最快的侦测到主进程的运行状态,而特点2则特点1的必然条件。除此之外,还有一个自己添加的特点,让守护进程是隐藏窗口模式运行,这样避免占用任务栏、桌面,也可以防止自己误关闭等。

    尽管没有窗口,但并不妨碍我们对守护进程进行操作,例如可以让守护进程开放一个http端口,通过网页来控制自己。当访问 127.0.0.1:5000/kill 时,则触发process.kill(process.pid)。

    由于不想用forever等模块,想尽可能简单的实现,所以使用了Win系统中,自带的wscript命令来创建守护进程。

    下面是大致的步骤:

    创建一个watch.vbs文件,只有一行内容

    CreateObject("WScript.Shell").Run "node watch",0

    请注意末尾的0,表示以隐藏模式运行,否则会产生一个CMD窗口。

    主程序 main.js:

    //your code
    //在30002端口侦听,守护进程启动时会发轮询过来
    require('net').createServer(function(net) {
        //应答请求,返回主进程PID
        net.write(process.pid.toString());
        net.pipe(net);
    }).listen(30002);
    //启动守护进程
    require('child_process').exec('wscript watch.vbs', {cwd:__dirname});

    实际上,上方最后一行代码中,wscript会确保vbs中启动的进程脱离终端控制。

    这是本文最关键的一行。

    守护进程 watch.js

    //代码较占版面,就不贴出来了: //动作:不停的setTimeout去尝试连接30002端口,根据结果,来选择是否去除hosts绑定。

    最后,在CMD中node main,会发现任务管理器中有两个node.exe进程,关掉当前的CMD,进程列表中仍有个node.exe驻留着,它即守护进程。

    完!

    nodeJS初学者,不知大家有没有更好的方案。目前来说,在Unix或Linux中,还未找到类似于Win这么简单的办法。否则只能尝试判断系统类型,然后选择forever模块了。

    源码地址:http://github.com/xiongsongsong/tds

    展开全文
  • Linux系统下创建守护进程(Daemon)

    万次阅读 2018-07-30 17:29:50
    守护进程(daemon)是指在...守护进程在后台运行,类似于Windows中的系统服务。    编写守护进程程序的要点:   (1)让程序在后台执行。方法是调用fork()产生一个子进程,然后使父进程退出。 (2)调用se...

    守护进程(daemon)是指在后台运行的,没有控制终端与之相连的进程。它独立于控制终端,周期性地执行某种任务。Linux的大多数服务器就是用守护进程的方式实现的。如web服务器进程http等。守护进程在后台运行,类似于Windows中的系统服务。

     

          编写守护进程程序的要点:

     

    (1)让程序在后台执行。方法是调用fork()产生一个子进程,然后使父进程退出。

    (2)调用setsid()创建一个新对话期。控制终端、登录会话和进程组通常是从父进程继承下来的,守护进程要摆脱它们,不受它们的影响,方法是调用setsid()使进程成为一个会话组长。setsid()调用成功后,进程成为新的会话组长和进程组长,并与原来的登录会话、进程组和控制终端脱离。

    (3)禁止进程重新打开控制终端。经过以上步骤,进程已经成为一个无终端的会话组长,但是它可以重新申请打开一个终端。为了避免这种情况发生,可以通过使进程不再是会话组长来实现。再一次通过fork()创建新的子进程,使调用fork的进程退出。

    (4)关闭不再需要的文件描述符。子进程从父进程继承打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误。首先获得最高文件描述符值,然后用一个循环程序,关闭0到最高文件描述符值的所有文件描述符。

    (5)将当前目录更改为根目录。

    (6)子进程从父进程继承的文件创建屏蔽字可能会拒绝某些许可权。为防止这一点,使用unmask(0)将屏蔽字清零。

    (7)处理SIGCHLD信号。对于服务器进程,在请求到来时往往生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie),从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将SIGCHLD信号的操作设为SIG_IGN。这样,子进程结束时不会产生僵尸进程。

     

          守护进程的实例:

    #include <stdio.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <signal.h>
    #include <sys/param.h>
    #include <sys/stat.h>
    #include <time.h>
    #include <syslog.h>
     
    int init_daemon(void) 
    { 
    	int pid; 
    	int i; 
     
    	//忽略终端I/O信号,STOP信号
    	signal(SIGTTOU,SIG_IGN);
    	signal(SIGTTIN,SIG_IGN);
    	signal(SIGTSTP,SIG_IGN);
    	signal(SIGHUP,SIG_IGN);
    	
    	pid = fork();
    	if(pid > 0) {
    		exit(0); //结束父进程,使得子进程成为后台进程
    	}
    	else if(pid < 0) { 
    		return -1;
    	}
     
    	//建立一个新的进程组,在这个新的进程组中,子进程成为这个进程组的首进程,以使该进程脱离所有终端
    	setsid();
     
    	//再次新建一个子进程,退出父进程,保证该进程不是进程组长,同时让该进程无法再打开一个新的终端
    	pid=fork();
    	if( pid > 0) {
    		exit(0);
    	}
    	else if( pid< 0) {
    		return -1;
    	}
     
    	//关闭所有从父进程继承的不再需要的文件描述符
    	for(i=0;i< NOFILE;close(i++));
     
    	//改变工作目录,使得进程不与任何文件系统联系
    	chdir("/mnt/hgfs/lalala/test");
     
    	//将文件当时创建屏蔽字设置为0
    	umask(0);
     
    	//忽略SIGCHLD信号
    	signal(SIGCHLD,SIG_IGN); 
    	
    	return 0;
    }
     
    int main() 
    { 
    	time_t now;
    	char buf[128]= {0};
    	init_daemon();
    	int count = 100;
    	
    	//syslog(LOG_USER|LOG_INFO,"TestDaemonProcess! \n");
    	system("echo \"TestDaemonProcess!\\n\" >> logspp");
    	FILE* fd = fopen("./logspp", "r+");
    	while(count--) { 
    		sleep(2);
    		time(&now); 
    		
    		//syslog(LOG_USER|LOG_INFO,"SystemTime: \t%s\t\t\n",ctime(&now));
    		
    		fprintf(fd, "SystemTime: \t%s\t\t\n",ctime(&now));
    		
    		
    	} 
    	
    	fclose(fd);
        return 0;
    }

    版本二:

    #include <stdio.h>
    #include <signal.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    
    #include <sys/param.h>
    
    #include <time.h>
    
    #if 1
    void daemon_init() 
    {
    	/*
    	创建守护进程
    	*/
    	int i;
    	pid_t pid;
    	signal(SIGINT,	SIG_IGN);// 终端中断  
    	signal(SIGHUP,	SIG_IGN);// 连接挂断  
    	signal(SIGQUIT, SIG_IGN);// 终端退出  
    	signal(SIGPIPE, SIG_IGN);// 向无读进程的管道写数据	
    	signal(SIGTTOU, SIG_IGN);// 后台程序尝试写操作	
    	signal(SIGTTIN, SIG_IGN);// 后台程序尝试读操作	
    	signal(SIGTERM, SIG_IGN);// 终止
    	pid = fork(); 
    	if(pid < 0) {  
    		perror("fork error!");	
    		exit(1);  
    	} else if(pid > 0) {  
    		exit(0);  
    	} 
    	setsid(); 
    	
    	
    	
    	char szPath[1024];	
    	if(getcwd(szPath, sizeof(szPath)) == NULL) {   
    		exit(1);  
    	} else {  
    		chdir(szPath);	
    	}
    	
    	umask(0);
    	for (i = 0; i < 3; ++i) {  
    		close(i);  
    	}
    	signal(SIGCHLD,SIG_IGN); 
    	
    //	printf("close stream....after \n"); //没用了
    }
    
    
    int main() 
    { 
    	time_t now;
    	char buf[128]= {0};
    	int count =100;
    	printf("pid = %d\n", getpid());
    	daemon_init();
    	
    	
    	//syslog(LOG_USER|LOG_INFO,"TestDaemonProcess! \n");
    	system("echo \"TestDaemonProcess!\\n\" >> logspp");
    	FILE* fd = fopen("./logspp", "r+");
    	while(count--) { 
    		sleep(2);
    		time(&now); 
    		
    		//syslog(LOG_USER|LOG_INFO,"SystemTime: \t%s\t\t\n",ctime(&now));
    		
    		fprintf(fd, "SystemTime: \t%s\t\t.. pid : %d\n",ctime(&now), getpid());
    		
    		
    	} 
    	
    	fclose(fd);
       return 0;
    }

    引申:

    什么是僵尸进程

    首先内核会释放终止进程(调用了exit系统调用)所使用的所有存储区,关闭所有打开的文件等,但内核为每一个终止子进程保存了一定量的信息。这些信息至少包括进程ID,进程的终止状态,以及该进程使用的CPU时间,所以当终止子进程的父进程调用wait或waitpid时就可以得到这些信息。

    而僵尸进程就是指:一个进程执行了exit系统调用退出,而其父进程并没有为它收尸(调用wait或waitpid来获得它的结束状态)的进程。

    任何一个子进程(init除外)在exit后并非马上就消失,而是留下一个称外僵尸进程的数据结构,等待父进程处理。这是每个子进程都必需经历的阶段。另外子进程退出的时候会向其父进程发送一个SIGCHLD信号。

     

    僵尸进程的目的?

    设置僵死状态的目的是维护子进程的信息,以便父进程在以后某个时候获取。这些信息至少包括进程ID,进程的终止状态,以及该进程使用的CPU时间,所以当终止子进程的父进程调用wait或waitpid时就可以得到这些信息。如果一个进程终止,而该进程有子进程处于僵尸状态,那么它的所有僵尸子进程的父进程ID将被重置为1(init进程)。继承这些子进程的init进程将清理它们(也就是说init进程将wait它们,从而去除它们的僵尸状态)。

     

    如何避免僵尸进程?

    1. 通过signal(SIGCHLD, SIG_IGN)通知内核对子进程的结束不关心,由内核回收。如果不想让父进程挂起,可以在父进程中加入一条语句:signal(SIGCHLD,SIG_IGN);表示父进程忽略SIGCHLD信号,该信号是子进程退出的时候向父进程发送的。
    2. 父进程调用wait/waitpid等函数等待子进程结束,如果尚无子进程退出wait会导致父进程阻塞waitpid可以通过传递WNOHANG使父进程不阻塞立即返回
    3. 如果父进程很忙可以用signal注册信号处理函数,在信号处理函数调用wait/waitpid等待子进程退出。
    4. 通过两次调用fork。父进程首先调用fork创建一个子进程然后waitpid等待子进程退出,子进程再fork一个孙进程后退出。这样子进程退出后会被父进程等待回收,而对于孙子进程其父进程已经退出所以孙进程成为一个孤儿进程,孤儿进程由init进程接管,孙进程结束后,init会等待回收。

    第一种方法忽略SIGCHLD信号,这常用于并发服务器的性能的一个技巧因为并发服务器常常fork很多子进程,子进程终结之后需要服务器进程去wait清理资源。如果将此信号的处理方式设为忽略,可让内核把僵尸子进程转交给init进程去处理,省去了大量僵尸进程占用系统资源。

     

    僵尸进程处理办法

    1 wait()函数

    #include <sys/types.h> 
    #include <sys/wait.h>

    pid_t wait(int *status);

    进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止。 
    参数status用来保存被收集进程退出时的一些状态,它是一个指向int类型的指针。但如果我们对这个子进程是如何死掉的毫不在意,只想把这个僵尸进程消灭掉,(事实上绝大多数情况下,我们都会这样想),我们就可以设定这个参数为NULL,就象下面这样:

      pid = wait(NULL);

    如果成功,wait会返回被收集的子进程的进程ID,如果调用进程没有子进程,调用就会失败,此时wait返回-1,同时errno被置为ECHILD。

    • wait系统调用会使父进程暂停执行,直到它的一个子进程结束为止。
    • 返回的是子进程的PID,它通常是结束的子进程
    • 状态信息允许父进程判定子进程的退出状态,即从子进程的main函数返回的值或子进程中exit语句的退出码。
    • 如果status不是一个空指针,状态信息将被写入它指向的位置

    可以上述的一些宏判断子进程的退出情况:

    QQ截图20130713110230

     

    2 waitpid()函数

    #include <sys/types.h> 
    #include <sys/wait.h>

    pid_t waitpid(pid_t pid, int *status, int options);

    参数:

    status:如果不是空,会把状态信息写到它指向的位置,与wait一样

    options:允许改变waitpid的行为,最有用的一个选项是WNOHANG,它的作用是防止waitpid把调用者的执行挂起

    The value of options is an OR of zero or more  of  the  following  con- 
    stants:

    WNOHANG     return immediately if no child has exited.

    WUNTRACED   also  return  if  a  child  has stopped (but not traced via 
                ptrace(2)).  Status for traced children which have  stopped 
                is provided even if this option is not specified.

    WCONTINUED (since Linux 2.6.10) 
                also return if a stopped child has been resumed by delivery 
                of SIGCONT.

    返回值:如果成功返回等待子进程的ID,失败返回-1

     

    对于waitpid的p i d参数的解释与其值有关:

    pid == -1 等待任一子进程。于是在这一功能方面waitpid与wait等效。

    pid > 0 等待其进程I D与p i d相等的子进程。

    pid == 0 等待其组I D等于调用进程的组I D的任一子进程。换句话说是与调用者进程同在一个组的进程。

    pid < -1 等待其组I D等于p i d的绝对值的任一子进程

     

     

    wait与waitpid区别:

    • 在一个子进程终止前, wait 使其调用者阻塞,而waitpid 有一选择项,可使调用者不阻塞。
    • waitpid并不等待第一个终止的子进程—它有若干个选择项,可以控制它所等待的特定进程。
    • 实际上wait函数是waitpid函数的一个特例。waitpid(-1, &status, 0);

    如以下代码会创建100个子进程,但是父进程并未等待它们结束,所以在父进程退出前会有100个僵尸进程。

    #include <stdio.h>  
    #include <unistd.h>  
       
    int main() {  
       
      int i;  
      pid_t pid;  
       
      for(i=0; i<100; i++) {  
        pid = fork();  
        if(pid == 0)  
          break;  
      }  
       
      if(pid>0) {  
        printf("press Enter to exit...");  
        getchar();  
      }  
       
      return 0;  
    }  

    其中一个解决方法即是编写一个SIGCHLD信号处理程序来调用wait/waitpid来等待子进程返回。

    #include <stdio.h>  
    #include <unistd.h>  
    #include <signal.h>  
    #include <sys/types.h>  
    #include <sys/wait.h>  
       
    void wait4children(int signo) {  
       
      int status;  
      wait(&status);  
       
    }  
       
    int main() {  
       
      int i;  
      pid_t pid;  
       
      signal(SIGCHLD, wait4children);  
       
      for(i=0; i<100; i++) {  
        pid = fork();  
        if(pid == 0)  
          break;  
      }  
       
      if(pid>0) {  
        printf("press Enter to exit...");  
        getchar();  
      }  
       
      return 0;  
    }  

    但是通过运行程序发现还是会有僵尸进程,而且每次僵尸进程的数量都不定。这是为什么呢?其实主要是因为Linux的信号机制是不排队的,假如在某一时间段多个子进程退出后都会发出SIGCHLD信号,但父进程来不及一个一个地响应,所以最后父进程实际上只执行了一次信号处理函数。但执行一次信号处理函数只等待一个子进程退出,所以最后会有一些子进程依然是僵尸进程。

    虽然这样但是有一点是明了的,就是收到SIGCHLD必然有子进程退出,而我们可以在信号处理函数里循环调用waitpid函数来等待所有的退出的子进程。至于为什么不用wait,主要原因是在wait在清理完所有僵尸进程后再次等待会阻塞。

     

    所以最佳方案如下:

    #include <stdio.h>  
    #include <unistd.h>  
    #include <signal.h>  
    #include <errno.h>  
    #include <sys/types.h>  
    #include <sys/wait.h>  
       
    void wait4children(int signo) {  
      int status;  
      while(waitpid(-1, &status, WNOHANG) > 0);  
    }  
       
    int main() {  
       
      int i;  
      pid_t pid;  
       
      signal(SIGCHLD, wait4children);  
       
      for(i=0; i<100; i++) {  
        pid = fork();  
        if(pid == 0)  
          break;  
      }  
       
      if(pid>0) {  
        printf("press Enter to exit...");  
        getchar();  
      }  
       
      return 0;  
    }  

    这里使用waitpid而不是使用wait的原因在于:我们在一个循环内调用waitpid,以获取所有已终止子进程的状态。我们必须指定WNOHANG选项,它告诉waitpid在有尚未终止的子进程在运行时不要阻塞。我们不能在循环内调用wait,因为没有办法防止wait在正运行的子进程尚有未终止时阻塞。

     

    文章参考:https://www.cnblogs.com/sky-heaven/p/8074273.html

    文章参考:https://blog.csdn.net/zyl_1102179268/article/details/72956407

    展开全文
  • 守护进程创建

    2019-03-10 08:30:59
    Linux的大多数服务器就是用守护进程实现的。比如,Internet服务器inetd,Web服务器httpd等。同时,守护进程完成许多系统任务。比如,作业规划进程crond,打印进程lpd等。(这里的结尾字母d就是Daemon的意思)1. 使...

    Linux的大多数服务器就是用守护进程实现的。比如,Internet服务器inetd,Web服务器httpd等。同时,守护进程完成许多系统任务。比如,作业规划进程crond,打印进程lpd等。(这里的结尾字母d就是Daemon的意思)

    1. 使进程在后台运行

    //创建子进程父进程退出
    if((pid = fork())>0)
       exit(0);
    else if(pid<0)
    {
        perror("fail to fork");
        exit(-1);
    }
    

    2. 脱离控制终端,登录会话和进程组(创建新会话)
    setsid()调用成功后,进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离

    setsid(); 
    

    3. 禁止进程重新打开控制终端
    现在,进程已经成为无终端的会话组长。但它可以重新申请打开一个控制终端。可以通过使进程不再成为会话组长来禁止进程重新打开控制终端。父进程(会话组长)退出,子进程继续执行,并不再拥有打开控制终端的能力

    if(pid=fork()) 
          exit(0);//结束第一子进程,第二子进程继续(第二子进程不再是会话组长)
    

    4. 关闭所有文件描述符
    进程从创建它的父进程那里继承了打开的文件描述符。如不关闭,将会浪费系统资源,造成进程所在的文件系统无法卸下以及引起无法预料的错误

    for(i=0;i<=getdtablesize();i++)
     close(i);
    

    5. 改变当前工作目录
    进程活动时,其工作目录所在的文件系统不能卸下。一般需要将工作目录改变到根目录。对于需要转储核心,写运行日志的进程将工作目录改变到特定目录如/tmp
    为什么改变工作目录,见lsof

    chdir("/tmp") ;
    

    6. 重设权限掩码
    进程从创建它的父进程那里继承了文件创建掩码。它可能修改守护进程所创建的文件的存取位。为防止这一点,将文件创建掩模清除

    umask(0);
    

    7. 处理SIGCHLD信号
    处理SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。如果父进程等待子进程结束,将增加父进程的负担,影响服务器进程的并发性能。在Linux下可以简单地将 SIGCHLD信号的操作设为SIG_IGN

    signal(SIGCHLD,SIG_IGN);
    

    8. 举例

    /*name: init_deamon.c
     *function:创建一个守护进程
     */
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h> 
    #include <signal.h> 
    #include <sys/param.h> 
    #include <sys/types.h> 
    #include <sys/stat.h> 
    void init_deamon(void) 
    { 
        int pid; 
        int i;
    
        /* 处理SIGCHLD信号。处理SIGCHLD信号并不是必须的。但对于某些进程,特别是服务器进程往往在请求到来时生成子进程处理请求。
        如果父进程不等待子进程结束,子进程将成为僵尸进程(zombie)从而占用系统资源。*/
        if(signal(SIGCHLD,SIG_IGN) == SIG_ERR){
            printf("Cant signal in init_daemon.");
            exit(1);
        }
        if(pid=fork()) 
            exit(0);//是父进程,结束父进程 
        else if(pid< 0){ 
            perror("fail to fork1");
            exit(1);//fork失败,退出
        }
        //是第一子进程,后台继续执行
        setsid();//第一子进程成为新的会话组长和进程组长 
        //并与控制终端分离 
        if(pid=fork()) 
            exit(0);//是第一子进程,结束第一子进程 
        else if(pid< 0) 
            exit(1);//fork失败,退出 
            
        //是第二子进程,继续 
        //第二子进程不再是会话组长 
    
        chdir("/tmp");//改变工作目录到/tmp 
        umask(0);//重设文件创建掩模 
        
        return; 
    }
    
    展开全文
  • windows守护进程实现

    千次阅读 2012-02-25 17:35:36
    有的时候需要设计一个可以同时在Windows和Linux下运行的守护进程 为了避免过多的修改代码。可以添加如下伪指令来实现: #ifndef WIN32 #pragma comment(linker,"subsystem:windows entry:mainCRTStartup") #...
  • 守护进程(daemon)是指在后台...守护进程在后台运行,类似于Windows中的系统服务。  编写守护进程程序的要点: (1)让程序在后台执行。方法是调用fork()产生一个子进程,然后使父进程退出。 (2)调用setsid()
  • Windows守护进程的一种简单实现

    万次阅读 2016-05-13 14:51:54
    一讲到守护进程,很多人都想到了Linux系统,确实在Windows上这个说的比较少。今天上午群里有个朋友问我了下Windows守护进程的实现问题,我想了想,简单用C++写了个小例子,用来实现系统开机自启动(注册表启动),...
  • Windows守护进程简单示例

    千次阅读 2018-12-28 15:41:47
    @描述:一个简单的Windows守护进程的例子(C++版本) @作者:kikaylee @日期:2016-05-13 10:30 */ #include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;Wind...
  • Windows下 tomcat的守护进程、心跳程序

    热门讨论 2010-05-14 14:29:08
    Windows下 tomcat的守护进程、心跳程序 可以自行重启tomcat
  • Windows进程守护工具

    千次阅读 2012-07-18 17:14:25
    1、进程守护.vbs 使用时需修改运行周期(10行)、守护进程名称(12行)和程序启动路径(22行)。 2、进程守护批处理.bat 使用时需修改守护进程名称和执行用户(3行)、程序启动路径(6行) vbs脚本运行时会创建...
  • 大多数的解决方法是使用其他进程来守护服务器程序,如果服务器程序挂了,通过守护进程来启动服务器程序。 万一守护进程挂了呢?使用双守护来提高稳定性,守护A负责监控服务器程序与守护B,守护B负责监控守护A,任何...
  • Python 编写windows守护进程程序

    万次阅读 2017-09-10 17:49:33
    为了谨防程序崩溃,特别编写一个守护进程程序,时刻监控程序是否崩溃并重新启动。 下面介绍两种实现方式。 二、第一种 监控进程PID方式 Demo.py 10秒关闭程序,模拟程序崩溃 # -*- coding: utf-8 -*- import log
  • 守护进程因为要开机启动,还要高权限,所以我就把它做成Windows服务了。 关于Windows服务的官方文档,大家可以看https://msdn.microsoft.com/en-us/library/windows/desktop/ms686953(v=vs.85).aspx。   ...
  • 近期,中心应用服务无故关闭。在检查系统和应用程序日志无果后采取了进程守护的...2、进程守护批处理.bat 使用时需修改守护进程名称和执行用户(3行)、程序启动路径(6行)vbs脚本运行时会创建wscript.exe进程,该进程...
  • Windows编程之双进程守护

    千次阅读 2017-12-26 20:25:39
    Windows编程之双进程守护Windows编程之双进程守护 需求分析 设计原理 相关函数 设计详细 方法1 方法2 项目测试情况 方法1 方法2 总结 需求分析设计实现双进程守护程序,当两个进程同时运行时,关闭其中任意一个,另...
  • windows环境下nssm配置tomcat守护进程

    千次阅读 2018-12-06 18:21:23
    导致服务不稳定,为此,急需寻找一种类似linux环境下的守护进程。 网上的资料很多,本人经过实战,总结出一种相对简单方法。 准备软件如下: 1、tomcat安装版:...
  • 守护进程

    2017-02-10 19:33:20
    守护进程值得是系统长期运行的后台进程,类似Windows服务。守护进程信息通过ps –a无法查看到,需要用到–x参数,当使 用这条命令的时候,往往还附上-j参数以查看作业控制信息,其中TPGID一栏为-1就是守护进程。 ...
  • Windows服务做守护进程

    千次阅读 2018-10-22 09:50:01
    // CreateService.cpp : 定义应用程序的...windows.h&gt; #include &lt;stdio.h&gt; #include &lt;string&gt; #include &lt;atomic&gt; #pragma comment(linker,"/subsystem:windo...
  • Windows+Cpp 守护进程

    千次阅读 2012-12-20 18:22:23
    用于监控另一个进程,发现该进程关掉了就自动把它重启。 可以用脚本程序,如vbs或者bat实现。下面程序使用C++实现:用于监视目标程序HTServer.exe,如果目标程序没有在运行,则运行目标程序。 代码如下: // ...
  • 如何创建和关闭一个守护进程

    千次阅读 2018-01-08 19:34:01
    如何创建和关闭一个守护进程 2018年1月8日 18:44    1守护进程特点:    1 独立性:独立于终端,用户    2 周期性:不断执行代码      2如何创建:    1 fork 结束主线程 2 ...
  • windows 服务与linux 守护进程

    千次阅读 2013-09-25 18:13:21
    Windows服务创建及安装 我们将研究如何创建一个作为Windows服务的应用程序。内容包含什么是Windows服务,如何创建、安装和调试它们。会用到System.ServiceProcess.ServiceBase命名空间的类。 什么是Windows...
  • 守护进程:与终端无关联的系统进程。 守护进程是脱离于终端并且在后台运行的进程。守护进程脱离于终端是为了避免进程在执行过程中的信息在任何终端上显示并且进程也不会被任何终端所产生的终端信息所打断。   在...
  • windows下用c++编写守护进程

    千次阅读 2016-11-24 01:21:51
    写一个C++程序,监控另一个进程,发现该进程关掉了就自动把它重启  #include #include #include #include using namespace std;  int _tmain(int argc, TCHAR *argv[])  {  STARTUPINFO si;  ...
  • 守护进程编写实例

    2018-07-13 09:31:00
    守护进程信息通过ps –a无法查看到,需要用到–x参数,当使用这条命令的时候,往往还附上-j参数以查看作业控制信息,其中TPGID一栏为-1就是守护进程创建守护进程最关键的一步是调用setsid函数创建一个新的Sessi...
  • 因为希望主服务随windows开机启动即启动,所以将守护进程和主进程设计成windows服务。 守护服务于主服务间的通讯方式: 1. socket 2. 管道(分为匿名管道和命名管道) 3. WM_COPYDATA消息  WM_COPYDATA是一种非常...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 34,767
精华内容 13,906
关键字:

windows创建守护进程