APUE---chap13守护进程

13.1 引言

守护进程没有控制终端,所以说是后台运行的。

13.2 守护进程的特征

ps -axj

-a 显示由其他用户所拥有的进程状态

-x 显示没有控制终端的进程状态

-j 显示与作业有关的信息

  • kthreadd表现为其他内核进程的父进程。
zion6135@zion6135-VirtualBox:~$ ps -axj | grep -Ei "kthread"
      0       2       0       0 ?             -1 S        0   0:00 [kthreadd]
   6913   11078   11077    6913 pts/0      11077 S+    1000   0:00 grep --color=auto -Ei kthread
  • 进程1通常是init进程,它是一个系统守护进程
zion6135@zion6135-VirtualBox:~$ ps -axj | grep -Ei "init"
      0       1       1       1 ?             -1 Ss       0   0:08 /sbin/init splash
   6913   11084   11083    6913 pts/0      11083 S+    1000   0:00 grep --color=auto -Ei init

13.3 编程规则

  • 1.调用umask将守护进程设定到希望的文件权限,确保文件权限是自己期望的

  • 2.调用fork(),然后父进程exit(),让守护进程不关联终端。父进程exit()让shell认为程序正常终止。另外子进程继承父进程的进程组ID,但是子进程不等于进程组ID。

  • 3.子进程调用setsid()创建新的会话;然后(1)子进程成为新会话的首进程。(2)子进程成为一个新进程组的组长进程。(3)没有控制终端

  • 4.为了子进程一直存在,需要chdir("/"); 让子进程的工作目录在一个一直存在的目录,因为我们希望子进程一直运行。

  • 5.关闭不需要的file descripter; 这让子进程不在继承来自父进程的文件描述符,可通过getrlimit()来关闭已经打开的文件描述符。

  • 6.某些守护进程打开/dev/null,让文件描述符0 1 2对应的标准输入,标准输出,标准错误都不会产生任何效果。

13.4 出错记录

  • 系统日志服务syslogd

sysog()函数的调用:

 level级别:一般系统日志会通过配置文件,决定代码里面高于什么level的日志会被写如系统日志文件。

  • 简单的例子,Ubuntu生成的日志在/var/log/syslog文件里面
#include <errno.h>
#include <string.h>
#include <syslog.h>

#define FNAME "/tmp/out"
int main() {
    openlog("lbwwww", LOG_PID, LOG_DAEMON);

    syslog(LOG_INFO, "daemon() sucess");
    printf("1111\n");
    syslog(LOG_ERR, "fopen:%s", strerror(errno));

    syslog(LOG_INFO, "%s was opend.", FNAME);
    syslog(LOG_DEBUG, "%d is printed", 2);
    sleep(1);

    exit(0);
}


//打印结果
lbw@HP-ZHAN-66-Pro-15-G3:~$ cat /var/log/syslog | grep -Ei "lbwww"
Feb 23 20:10:32 tstxb-HP-ZHAN-66-Pro-15-G3 lbwwww[462105]: daemon() sucess
Feb 23 20:10:32 tstxb-HP-ZHAN-66-Pro-15-G3 lbwwww[462105]: fopen:Success
Feb 23 20:10:32 tstxb-HP-ZHAN-66-Pro-15-G3 lbwwww[462105]: /tmp/out was opend.
Feb 23 20:10:32 tstxb-HP-ZHAN-66-Pro-15-G3 lbwwww[462105]: 2 is printed
  • 有三种产生日志消息的方法:

1.内核例程可调用log()函数,任何一个进程可open()然后read("/dev/kog"  ..)来读取日志。

2.大多数守护进程调用syslog(3)函数来产生日志消息,从而让消息被发送到套接字/dev/log

3.可将日志消息发送到UDP的514端口

  • 通常syslogd守护进程读取上述的3种格式的日志消息。通常这个守护进程启动时读取配置文件/etc/syslog.conf文件,该文件决定了不同种类的消息被送到何处,。

13.5 单实例的守护进程

  • 守护进程实例
// 守护进程
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

static int daemonize() {
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork()");
        return -1;
    }
    if (pid > 0) {  // parent
        exit(0);
    }
    int fd = open("/dev/null", O_RDWR);
    if (fd < 0) {
        perror("open()");
        return -1;
    }

    dup2(fd, 0);
    dup2(fd, 1);
    dup2(fd, 2);
    if (fd > 2) {
        close(fd);
    }
    setsid();  // child

    // 让守护进程在一定存在的目录
    chdir("/");
    // umask(0);
    return 0;
}
#define FNAME "/tmp/out"
int main() {
    if (daemonize()) {
        exit(1);
    }
    FILE *fp = fopen(FNAME, "w");
    if (fp == NULL) {
        perror("fopen()");
        exit(1);
    }

    for (int i = 0;; i++) {
        fflush(fp);
        fprintf(fp, "%d\n", i);
        sleep(1);
    }

    exit(0);
}
// 父进程执行结束,子进程后台运行
// lbw@lbw:~/lbw/gitNote/chap9$ g++ main.c -o mydaemon
// lbw@lbw:~/lbw/gitNote/chap9$ ./mydaemon
// lbw@lbw:~/lbw/gitNote/chap9$

// 查看子进程(守护进程)
// lbw@lbw:~/lbw/gitNote/chap9$
// lbw@lbw:~/lbw/gitNote/chap9$ps axj
// 96186 4160831 4160831 4160831 ? -1 Ss 1000 0 : 00. / mydaemon

//查看输出的日志
// lbw@lbw:~/lbw/gitNote/chap9$:tail -f /tmp/out
// 158
// 159
// 160
// 161
// 162
// 163
// 164
// 165

//杀死守护进程
// lbw@lbw:~/lbw/gitNote/chap9$kill -9 4160831
  • 守护进程需要是有且唯一的。通过锁文件/var/run/name.pid来控制进程的单实例的目的

比如下面的ssh.pid可以看出文件内容是ssh的pid,通过ssh.pid文件可以达到单实例的目的。

​
lbw@HP-ZHAN-66-Pro-15-G3:/var/run$ ls
acpid.pid      crond.pid        docker.pid   libvirt         openvpn            spice-vdagentd   thermald     utmp
acpid.socket   crond.reboot     docker.sock  libvirtd.pid    openvpn-client     sshd             tmpfiles.d   uuidd
alsa           cups             gdm3         lock            openvpn-server     sshd.pid         udev         vmblock-fuse
avahi-daemon   dbus             gdm3.pid     log             plymouth           sudo             udisks2      vmware
blkid          dmeventd-client  initctl      lvm             sendsigs.omit.d    systemd          usbmuxd      wpa_supplicant
console-setup  dmeventd-server  initramfs    mount           shm                teamviewerd.ipc  usbmuxd.pid  xtables.lock
containerd     docker           irqbalance   NetworkManager  speech-dispatcher  teamviewerd.pid  user
lbw@HP-ZHAN-66-Pro-15-G3:/var/run$ cat sshd.pid
1232
lbw@HP-ZHAN-66-Pro-15-G3:/var/run$ ps axf | grep sshd.pid
1507627 pts/0    S+     0:00  |   |       \_ grep --color=auto sshd.pid
lbw@HP-ZHAN-66-Pro-15-G3:/var/run$ ps axf | grep sshd
   1232 ?        Ss     0:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
1520618 pts/0    S+     0:00  |   |       \_ grep --color=auto sshd
lbw@HP-ZHAN-66-Pro-15-G3:/var/run$ 

​

13.6 守护进程的惯例

  • 若守护进程用锁文件,锁文件通常存在/var/run目录下,名字一般叫name.pid(name是守护进程的名字),比如cron守护进程锁文件的名字是/var/run/cron.pid
  • 若守护进程的配置选项,该配置文件在/etc目录。比如syslogd守护进程的配置文件/etc/syslog.conf
  • 开机自启:守护进程可用命令行启动,但一般是由系统初始化脚本之一(/etc/rc*或/etc/init.d/*)启动的。
  • 如果希望守护进程终止的时候自动重启,我们就可在/etc/inittab中为该守护进程包括respawn记录项。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值