进程组与会话

进程组

进程组,也称之为作业,顾名思义,代表一个或多个进程的集合,设计进程组的概念是为了简化对多个进程的管理。
父进程创建子进程的时候,默认父子进程位于同一个进程组,进程组的ID就是第一个进程的ID,第一个进程成为进程组的组长。
只要进程组有一个进程存在,进程组就存在,与组长进程是否终止无关。
进程组生存期:进程组的最后一个进程终止。
一个进程可以为自己或者子进程设置进程组ID

进程组操作函数

  • getpgrp 获取当前进程的进程组ID
  • getpgid 获取指定进程的进程组ID
  • setpgid 改变进程默认所属的进程组,通常可用来加入一个现有的进程组或创建一个新进程组

会话###

会话是一个进程组或者多个进程组的集合,通常一个会话开始于用户登录,终止与用户退出,在此期间,该用户运行的所有程序都属于这个会话
创建一个会话需要注意以下几个事项:

  • 创建会话的进程不能是某个进程组的组长
  • 创建会话的进程成为这个会话的会长和会话里第一个进程组的组长
  • 新会话丢弃原有的控制终端,该会话没有控制终端
  • 建立新会话步骤,先fork一个子进程,然后父进程退出,子进程调用setsid

会话相关的两个函数:

  • getsid 查看当前进程的会话ID
  • setsid 创建一个会话,并以自己的ID为会话ID

守护进程

daemon进程,即守护进程是指linux后台服务程序,通常独立于控制终端并且周期性的执行某种任务,名字一般以d结尾
linux的系统服务进程,没有控制终端,不能直接和用户交互等都是守护进程,如nfs服务器,ftp服务器

创建守护进程模型

创建守护进程的过程实际上是创建一个新的会话,跳出控制终端建立的会话,达到脱离于终端运行的目的。创建守护进程模型有以下几个步骤:

  • 创建子进程,退出父进程
  • 子进程调用setsid创建会话,成为会话第一个进程组的组长
  • 改变当前目录为根目录,不是必须,为了防止当前目录被删除
  • 重设文件权限掩码umask为0
  • 重定向标准输入,标准输出和标准错误到/dev/null
  • 开始守护进程的逻辑

示例代码

/*
 * 创建一个守护进程
 * 
 */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>

int main()
{
	pid_t pid;
	int i;
	pid = fork();
	
	/* 干掉父进程 */
	if (pid > 0)
	{
		exit(0);
	}
	
	/* 创建会话期 */
	pid = setsid();
	printf("pid = %d \n", getpid());
	if (pid == -1)
	{
		perror("setsid error :");
		exit(0);
	}
	
	/* 改变文件掩码 */
	umask(0);
		
	/* 切换目录到根目录 */
	chdir("/");
	
	/* 重定位标准输入、输出、错误到/dev/null */
	for (i=0; i<3; i++)
	{
		close(i);
	}
	open("/dev/null",O_RDWR);
	dup(0);
	dup(0);
	
	int fd;
	time_t t;
	char *t_buffer;
	fd = open("timelog", O_CREAT | O_RDWR, 0644);
	if (fd == -1)
	{
		perror("fd error:");
		exit(-1);
	}
	while (1)
	{
		t = time(0);
		t_buffer = asctime(localtime(&t));
		write(fd, t_buffer, strlen(t_buffer));
		sleep(5);
	}
}

这个程序首先创建了一个守护进程,在守护进程里每隔5秒打印当前系统的时间到timelog日志文件里。
由于切换工作目录为根目录,因此执行的时候需要加上sudo权限,运行结果:

可以看到,这个守护进程的所属用户已经变成了root用户,父进程为1号进程,tty为?,表示不属于任何终端

使用sudo killall + 守护进程的名字可以杀掉守护进程

daemon函数

除了上述方法可以创建一个守护进程外,也可以使用daemon函数创建一个守护进程。

int daemon(int nochdir, int noclose);

参数:

  • nochdir:=0 将当前目录更改至“/”
  • noclose:=0 将标准输入、标准输出、标准错误重定向至“/dev/null”

返回值

  • 成功返回0
  • 失败返回-1

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

线程 上一篇
mmap 下一篇