【Linux系统编程】守护进程、线程

------------->【Linux系统编程/网络编程】(学习目录汇总) <--------------

1.守护进程

守护进程(Daemon Process),也就是通常说的Daemon(精灵)进程,是 Linux 中的后台服务进程,通常独立于控制终端并且周期性地执行某种任务或等待处理 某些发生的事件。一般采用以 d 结尾的名字。

Linux 后台的一些系统服务进程,没有控制终端,不能直接和用户交互。不受用户登录、注销的影响,一直在运行着,他们都是守护进程。如:预读入缓输出机制的实现;ftp 服务器;nfs 服务器等。

创建守护进程,最关键的一步是调用 setsid 函数创建一个新的 Session,并成为 Session Leader。

1.1 进程组

多个进程的集合就是进程组,也称之为作业。BSD 于 1980 年前后向 Unix 中增加的一个新特性。代表一个或多个进程的集合。每个进程都属于一个进程组。在waitpid函数和 kill 函数的参数中都曾使用到。操作系统设计的进程组的概念,是为了简化对多个进程的管理。

当父进程,创建子进程的时候,默认子进程与父进程属于同一进程组。进程组 ID == 第一个进程 ID(组长进程)。 所以,组长进程标识:其进程组 ID == 其进程 ID

可以使用 kill -SIGKILL -进程组 ID(负的)来将整个进程组内的进程全部杀死。
组长进程可以创建一个进程组,创建该进程组中的进程,然后终止。只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关。

进程组生存期:进程组创建到最后一个进程离开(终止或转移到另一个进程组)。

一个进程可以为自己或子进程设置进程组 ID

下面介绍几个常用的进程组函数:

得到当前进程所在的进程组的组 ID

pid_t getpgrp(void);

获取指定的进程所在的进程组的组 ID,参数 pid 就是指定的进程

pid_t getpgid(pid_t pid);

将某个进程移动到其他进程组中或者创建新的进程组

int setpgid(pid_t pid, pid_t pgid);

函数参数:

pid: 某个进程的进程 ID

pgid: 某个进程组的组 ID

如果 pgid 对应的进程组存在,pid 对应的进程会移动到这个组中,pid != pgid

如果 pgid 对应的进程组不存在,会创建一个新的进程组,因此要求 pid == pgid, 当前进程就是组长了

函数返回值:

函数调用成功返回 0,失败返回 - 1

1.2 会话

会话 (session) 是由一个或多个进程组组成的,一个会话可以对应一个控制终端,也可以没有。一个普通的进程可以调用 setsid() 函数使自己成为新 session 的领头进程(会长),并且这个 session 领头进程还会被放入到一个新的进程组中。先来看一下 setsid() 函数的原型:

1.3 setsid()函数

创建一个会话,并以自己的 ID 设置进程组 ID,同时也是新会话的 ID。

函数原型

#include <unistd.h>
pid_t setsid(void);

函数返回值

  • 成功:返回调用进程的会话 ID
  • 失败:-1,设置 errno

1.4 getsid()函数

获取进程所属的会话 ID

函数原型

#include <unistd.h>
pid_t getsid(pid_t pid);

函数返回值

  • 成功:返回调用进程的会话 ID
  • 失败:-1,设置 errno

pid 为 0 表示察看当前进程 session ID

使用这两个函数的注意事项:

1.调用这个函数的进程不能是组长进程,如果是,该函数调用失败,如何保证这个函数能调用成功呢?

  • 先 fork () 创建子进程,终止父进程,让子进程调用这个函数

2.如果调用这个函数的进程不是进程组长,会话创建成功

  • 这个进程会变成当前会话中的第一个进程,同时也会变成新的进程组的组长
  • 该函数调用成功之后,当前进程就脱离了控制终端,因此不会阻塞终端

1.5 创建守护进程

  1. 创建子进程,父进程退出
  2. 在子进程中创建新会话 setsid()函数 使子进程完全独立出来,脱离控制
  3. 改变当前目录位置 chdir()函数 防止占用可卸载的文件系统, 也可以换成其它路径
  4. 重设文件权限掩码 umask()函数 防止继承的文件创建屏蔽字拒绝某些权限,增加守护进程灵活性
  5. 通常根据需要,关闭/重定向 文件描述符
  6. 开始执行守护进程业务逻辑,通常是while()循环

小案例

写一个守护进程,每隔 2s 获取一次系统时间,并将得到的时间写入到磁盘文件中。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/time.h>
#include <time.h>

// 信号的处理动作
void writeFile(int num)
{
   
    // 得到系统时间
    time_t seconds = time(NULL);
    // 时间转换, 总秒数 -> 可以识别的时间字符串
    struct tm* loc = localtime(&seconds);
    // sprintf();
    char* curtime = asctime(loc); // 自带换行
    // 打开一个文件, 如果文件不存在, 就创建, 文件需要有追加属性
    // ./对应的是哪个目录? /home/robin
    // 0664 & ~022
    int fd = open("./time+++++++.log", O_WRONLY|O_CREAT|O_APPEND, 0664);
    write(fd, curtime, strlen(curtime));
    close(fd);
}
void sys_err(const char *str)  
{
     
  perror(str);  
  exit(1);  
} 

int main()
{
   
  	int ret;
    // 1. 创建子进程, 杀死父进程
    pid_t pid = fork();
    if(pid > 0)
    {
   
        // 父进程终止
        exit(0); // kill(getpid(), 9); raise(9); abort();
    }

    // 2. 子进程, 将其变成会话, 脱离当前终端
    pid = setsid();
		if(pid == -1)
    {
   
      sys_err("setsid error\n");
    }
    // 3. 修改进程的工作目录, 修改到一个不能被修改和删除的目录中 /home/robin
    ret = chdir("/home/robin");
		if(ret == -1)
    {
   
      sys_err("chdir error\n")
    }
    // 4. 设置掩码, 在进程中创建文件的时候这个掩码就起作用了
    umask(022);

    // 5. 重定向和终端关联的文件描述符 -> /dev/null
    int fd = open("/dev/null", O_RDWR);
    dup2(fd, STDIN_FILENO);
    dup2(fd, STDOUT_FILENO);
    dup2(fd, STDERR_FILENO);

    // 5. 委托内核捕捉并处理将来发生的信号-SIGALRM(14)
    struct sigaction act;
    act.sa_flags = 0;
    act.sa_handler = writeFile;
    sigemptyset(&act.sa_mask);
    sigaction(SIGALRM, &act, NULL);

    // 6. 设置定时器
    struct itimerval val;
    val.it_value.tv_sec = 2;
    val.it_value.tv_usec = 0;
    val.it_interval.tv_sec = 2;
    val.it_interval.tv_usec = 0;
    setitimer(ITIMER_REAL, &val, NULL);

    while(1)
    {
   
        sleep(100);
    }

    return 0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BillySturate

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值