经典C/S服务器模型之守护进程

linux编程-守护进程编写

守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进程。

Linux的大多数服务器就是用守护进程实现的。比如,Internet服务器inetd,Web服务器httpd等。同时,守护进程完成许多系统任务。比如,作业规划进程crond,打印进程lpd等。

守护进程的编程本身并不复杂,复杂的是各种版本的Unix的实现机制不尽相同,造成不同 Unix环境下守护进程的编程规则并不一致。

需要注意,照搬某些书上的规则(特别是BSD4.3和低版本的System V)到Linux会出现错误的。

基本概念及特性

进程:

系统进行资源分配和CPU调度的单位.函数getpid可以得到进程的进程ID:pid_t getpid(void);函数getppid可以得到进程的父进程ID:pid_t getppid(void);

① 每个进程都有一个父进程

② 当子进程终止时,父进程会得到通知并能取得子进程的退出状态.

进程组:

进程组是一个或多个进程的集合。它们与同一作业相关联,可以接受来自同一终端的各种信号。每个进程组都有唯一的进程组ID。函数getpgrp可以得到进程的进程组ID。

pid_t getpgrp(void);

每个进程组都可以有一个组长进程。组长进程的标识是,其进程组ID等于进程ID。

① 每个进程也属于一个进程组。

② 每个进程主都有一个进程组号,该号等于该进程组组长的PID号

③ 一个进程只能为它自己或子进程设置进程组ID号

会话期:

对话期(session)是一个或多个进程组的集合。函数getsid返回会话首进程的进程组ID。此函数是Single UNIX Specification的XSI扩展。pid_t getsid(pid_t pid);

如果pid是0,返回调用进程的会话首进程的进程组ID。如果pid并不属于调用者所在的会话,那么调用者就不能得到该会话首进程的进程组ID。

① setsid()函数可以建立一个对话期:

② 如果,调用setsid的进程不是一个进程组的组长,此函数创建一个新的会话期。

(1)此进程变成该新的对话期的首进程

(2)此进程变成一个新进程组的组长进程。

(3)此进程没有控制终端,如果在调用setsid前,该进程有控制终端,那么与该终端的联系被解除。如果该进程是一个进程组的组长,此函数返回错误。

(4)为了保证这一点,我们先调用fork()然后exit(),此时只有子进程在运行,子进程继承了父进程的进程组ID,但是进程PID却是新分配的,所以不可能是新会话的进程组的PID。

控制终端:
  linux是一个多用户多任务的分时操作系统,必须要支持多个用户同时登陆同一个操作系统,当一个用户登陆一次终端时就会产生一个会话,
  每个会话有一个会话首进程,即创建会话的进程,建立与终端连接的就是这个会话首进程,也被称为控制进程。

pid_t tcgetpgrp(int filedes);

函数tcgetpgrp返回前台进程组的进程组ID,该前台进程组与在filedes上打开的终端相关联;如果进程有一个控制终端,则该进程可以调用tcsetpgrp将前台进程组ID设置为pgrpid,pgrpid的值应该是在同一会话中的一个进程组的ID,filedes必须引用该会话的控制终端。

下图可以表示以上四者的基本关系:

   会话和进程组有一些特性:

  1). 一个会话可以有一个控制终端(controlling terminal)。

  2). 建立与控制终端连接的会话首进程被称为控制进程(controlling process)。

  3). 一个会话中的几个进程组可被分成一个前台进程组(forkground process group)和几个后台进程组(background process group)。

  4). 如果一个会话有一个控制终端,则它有一个前台进程组。

  5). 无论何时键入终端的中断键(DELETE或Ctrl+C),就会将中断信号发送给前台进程组的所有进程。

  6). 无论何时键入终端的退出键(Ctrl+\),就会将退出信号发送给前台进程组的所有进程。

  7). 如果终端检测到调制解调器(或网络)已经断开连接,则将挂断信号发送给控制进程(会话首进程)。

下边就以守护进程的实际代码运行,辅助理解。

 1 #include<unistd.h>
 2 #include<signal.h>
 3 #include<stdio.h>
 4 #include<stdlib.h>
 5 #include<sys/param.h>
 6 #include<sys/types.h>
 7 #include<sys/stat.h>
 8 #include<time.h>
 9
10 void init_daemon()
11 {
12         int pid;
13         int i;
14
15 //       for(i=0;i<NOFILE;i++)
16   //              close(i);
17
18         printf("parent\n");
19         printf("pid[%d]\n",getpid());
20         printf("ppid[%d]\n",getppid());
21         printf("gid[%d]\n",getpgrp());
22         printf("sid[%d]\n",getsid(0));
23         printf("tcid[%d]\n",tcgetpgrp(0));
24
25         printf("\n\n");
26         pid=fork();
27
28         if(pid<0)
29                 exit(1);//使得子进程一定不是进程组组长,这样才能调用setsid,建立新的进程组和会话组
30         else if(pid>0)
31                 exit(0);
32
33         printf("child 1\n");
34         printf("pid[%d]\n",getpid());
35         printf("ppid[%d]\n",getppid());
36         printf("gid[%d]\n",getpgrp());
37         printf("sid[%d]\n",getsid(0));
38         printf("tcid[%d]\n",tcgetpgrp(0));
39         printf("\n\n");
40         else if(pid>0)
41                 exit(0);
42
43         printf("child 1\n");
44         printf("pid[%d]\n",getpid());
45         printf("ppid[%d]\n",getppid());
46         printf("gid[%d]\n",getpgrp());
47         printf("sid[%d]\n",getsid(0));
48         printf("tcid[%d]\n",tcgetpgrp(0));
49         printf("\n\n");
50         setsid(); //建立新的进程组和会话组,并成为新的进程组的组长,和回话组的组长
51         printf("setsid child 1\n");
52         printf("pid[%d]\n",getpid());
53         printf("ppid[%d]\n",getppid());
54         printf("gid[%d]\n",getpgrp());
55         printf("sid[%d]\n",getsid(0));
56         printf("tcid[%d]\n",tcgetpgrp(0));
57         printf("\n\n");
58         pid=fork();
59         if(pid<0)
60                 exit(1);
61         else if(pid>0)
62                 exit(0);//使得孙子进程不在是进程组的组长,即没有权限建立新的与回话组绑定的控制终端
63
64         printf("child 2\n");
65         printf("pid[%d]\n",getpid());
66         printf("ppid[%d]\n",getppid());
67         printf("gid[%d]\n",getpgrp());
68         printf("sid[%d]\n",getsid(0));
69         printf("tcid[%d]\n",tcgetpgrp(0));
70         printf("\n\n");
71         //关闭文件描述符,这样进程不在与文件描述符传递数据,比如printf打印的数据不在现在是终端界面中
72         for(i=0;i<NOFILE;i++)
73                 close(i);
74
75         printf("close fd\n");
76         chdir("/home/cz/Desktop/mcs/");  //切换工作目录
77         printf("cd\n");
78         umask(0);//清除文件掩膜
79         printf("umask\n");
80 }
81
82 void main()
83 {
84         FILE *fp;
85         time_t t;
86
87         printf("%s\n","start");
88         init_daemon();
89 }
[email protected]:~/Desktop/mcs$ ./demaontest
start
parent
pid[2724]
ppid[1821]
gid[2724]
sid[1821]
tcid[2724]

[email protected]:~/Desktop/mcs$ child 1
pid[2725]
ppid[1]
gid[2724]
sid[1821]
tcid[1821]

setsid child 1
pid[2725]
ppid[1]
gid[2725]
sid[2725]
tcid[-1]

child 2
pid[2726]
ppid[1]
gid[2725]
sid[2725]
tcid[-1]

由以上实验结果就可以清晰的明白,守护进程化过程中,每一步的作用。

时间: 2024-12-13 12:57:28

经典C/S服务器模型之守护进程的相关文章

Linux进程实践(5) --守护进程

概述 守护进程是在需要在后台长期运行不受终端控制的进程,通常情况下守护进程在系统启动时自动运行,在服务器关闭的时候自动关闭:守护进程的名称通常以d结尾,比如sshd.xinetd.crond.atd等. 守护进程编程规则 调用umask将文件模式创建屏蔽字设置为一个已知值(通常是0) 调用fork(),创建新进程,它会是将来的守护进程 然后使父进程exit,保证子进程不是进程组组长 调用setsid创建新的会话 会话:是一个或者多个进程组的集合,通常一个会话开始与用户登录,终止于用户退出.在此期

【搞懂Java多线程之二】多线程调度及守护进程

在前一篇文章中说到,所有处在就绪状态中的线程,操作系统会选择优先级最高的优先进行调度,那么是不是优先级高的线程就一定比优先级低的线程先执行呢?线程的优先级又是怎么划分的呢?这篇文章,楼楼就要来说说这个问题啦!欢迎关注我的个人博客主页www.anycodex.com 1.线程的优先级 在Java中,线程优先级的范围为0-10,整数值越大,说明优先级更高. 几个相关的宏定义: MAX_PRIORITY 10,最高优先级 MIN_PRIORITY 1,最低优先级 NORM_PRIORITY 5,默认优

Python setdaemon守护进程

setdaemon守护进程 #_*_coding:utf-8_*_ __author__ = 'gaogd' import time import threading ''' 守护进程,如果主线程down了,子线程也就没有了. 下面先通过主进程生成main主线程,之后main主线程再生成10个子线程. ''' ''' def run(num):     if not num == 5:         time.sleep(1)     print 'Hi, I am thread %s..la

转:linux守护进程的启动方法

Linux 守护进程的启动方法 作者: 阮一峰 日期: 2016年2月28日 "守护进程"(daemon)就是一直在后台运行的进程(daemon). 本文介绍如何将一个 Web 应用,启动为守护进程. 一.问题的由来 Web应用写好后,下一件事就是启动,让它一直在后台运行. 这并不容易.举例来说,下面是一个最简单的Node应用server.js,只有6行. var http = require('http'); http.createServer(function(req, res)

Python实例浅谈之五Python守护进程和脚本单例运行

一.简介 守护进程最重要的特性是后台运行:它必须与其运行前的环境隔离开来,这些环境包括未关闭的文件描述符.控制终端.会话和进程组.工作目录以及文件创建掩码等:它可以在系统启动时从启动脚本/etc/rc.d中启动,可以由inetd守护进程启动,也可以有作业规划进程crond启动,还可以由用户终端(通常是shell)执行. Python有时需要保证只运行一个脚本实例,以避免数据的冲突. 二.Python守护进程 1.函数实现 #!/usr/bin/env python #coding: utf-8

#python#守护进程的实现

找了整天,终于找到一个可以用的代码 #! /usr/bin/env python2.7 #encoding:utf-8 #@description:一个python守护进程的例子 #@tags:python,daemon import sys import os import time import atexit from signal import SIGTERM      class Daemon:   """   A generic daemon class.     

linux守护进程

#include <iostream>#include <unistd.h>//#include "curl/curl.h"#include "app_curl.h"#include "youtube_package.h"#include "CAutoMail.h"#include <fcntl.h>#include <signal.h>#include <unistd.h

C#开发Linux守护进程

C#开发Linux守护进程 Linux守护进程是Linux的后台服务进程,相当于Windows服务,对于为Linux开发服务程序的朋友来说,Linux守护进程相关技术是必不可少的,因为这个技术不仅仅是为了开发守护进程,还可以拓展到多进程,父子进程文件描述符共享,父子进程通讯.控制等方面,是实现Linux大型服务的基础技术之一. 去年我也曾写了一篇关于守护进程的帖子,名字叫<.NET跨平台实践:用C#开发Linux守护进程>,这篇文章的的确确实现了一个Daemon,不过,它有一个弱点,不能运行多

守护进程的创建过程

编写守护进程需要5步 1 创建子进程,父进程结束(让这个进程由init进程托管) pid = fork(); if(pid > 0) //父进程 { exit(0); }2 在子进程中创建新会话(此进程就可以脱离原来进程,脱离控制终端,脱离原来进程组) setsid(); //最主要是脱离控制终端 3 改变当前目录(每一个进程都有一个当前目录), 不是必须的 chdir("/tmp"); 4 重新设置文件权限掩码(不是必须的) umask(0); 5 关闭打开的文件描述符(如果父