进程关系

进程组

进程组是一个或多个进程的集合。通常,它们是在同一作业中结合起来的,同一进程组中的各进程接收来自同一终端的各种信号,每个进程组有一个唯一的进程组ID。每个进程组有一个组长进程,该组长进程的ID等于进程组ID。从进程组创建开始到最后一个进程离开为止的时间称为进程组的生命周期

#include <unistd.h>

pid_t getpgrp(void);

返回值:调用进程的进程组ID

int setpgid(pid_t pid, pid_t pgid);

返回值:成功,返回0;失败,返回-1

说明:

setpgid用于添加进程到一个现有的进程组,或者创建一个新的进程组。

函数将进程ID为pid的进程加入ID为pgid的进程组中。

如果pid == pgid,则pid指定的进程变为进程组长;

如果pid == 0,则使用调用者的进程ID;

如果pgid == 0,则将pid用作进程组ID。

会话

会话是一个或者多个进程组的集合。进程组通常是由shell管道编制在一起的,如下:

proc1 | proc2 &

proc3 | proc4 | proc5

进程调用setsid函数创建一个新的会话:

#include <unistd.h>

pid_t setsid(void);

返回值:成功,返回进程组ID;失败,返回-1

说明:

如果调用此函的进程不是一个进程组长,则此函数创建一个新的会话,具体如下:

(1)   该进程变为新会话的会话首进程。

(2)   该进程成为一个新进程组的组成进程,新进程组ID是该进程ID。

(3)   该进程没有控制终端。

pid_t getsid(pid_t pid);

返回值:成功,返回会话首进程的进程组ID;失败,返回-1

说明:

getsid(0)返回调用进程的会话首进程的进程组ID,如果pid不属于调用者所在的会话,则不返回。

控制终端

一个会话可以有一个控制终端,通常是终端设备或伪终端设备。建立与控制终端连接的会话首进程被称为控制进程。一个会话中的进程组可被分为一个前台进程组和一个后台进程组。需要有一种方法通知内核哪一个进程组是前台进程组,这样便于终端设备驱动程序知道将终端输入和终端产生的信号发送到何处:

#include <unistd.h>

pid_t tcgetpgrp(int fd);

返回值:成功,返回前台进程组ID;失败,返回-1

int tcsetpgrp(int fd, pid_pgrpid);

返回值:成功,返回0;失败,返回-1

说明:

其中,fd为相关联的打开终端。大多数应用程序并不直接调用这两个函数,而是由作业控制shell调用。

需要管理控制终端的应用程序可以调用tcgetsid函数获得控制终端的会话首进程的进程组ID:

#include <termios.h>

pid_t tcgetsid(int fd);

返回值:成功,返回会话首进程的进程组ID;失败,返回-1

作业控制

作业控制允许在一个终端上启动多个作业(进程组),哪一个作业可以访问该终端以及哪些作业在后台运行。从shell使用作业控制的角度看,用户可以在前台或者后台启动一个作业,例如:

vi main.c在前台启动只有一个进程组成的作业,而make all &在后台启动只有一个进程组成的作业。

我们可以键入3个特殊字符使得终端程序产生信号,并将它们发送到前台进程组:

中断字符(Ctrl + C)产生SIGINT信号;

退出字符(Ctrl + \)产生SIGQUIT信号;

挂起字符(Ctrl + Z)产生SIGTSTP信号;

只有前台作业才可以接收终端上输入的字符,如果后台作业试图都终端,那么终端驱动程序向后台作业发送特定信号SIGTTIN,该信号将停止此后台作业,而shell则向用户发送通知,然后用户就可以利用shell命令fg将此作业转为前台作业运行。但是如果后台作业输出到控制终端又将发生什么呢?我们可以通过stty命令禁止这种情况。此时,终端驱动程序向后台作业发送SIGTTOU信号,使其进程阻塞,当然此时我们也可以利用fg将其移到前台运行。

守护进程

守护进程(daemon)常常在系统引导装入时启动,在系统关闭时终止。由于守护进程没有控制终端(其终端名设置为?),因此,其在后台运行。大多守护进程都以超级用户权限执行。

编程规则

(1)   首先调用umask将文件模式创建屏蔽字设置为一个已知数值(通常是0)。这样做是防止继承而来的屏蔽字没有某些权限,尤其是写权限。

(2)   调用fork,然后使父进程exit。

(3)   调用setsid创建一个新会话。

(4)   将当前工作目录更改为根目录。因为守护进程通常在系统引导之前就存在,如果守护进程的当前工作目录在一个需要挂载的文件系统上,那么该文件系统不能被卸载。也有某些守护进程会把当前工作目录更改到某个指定的位置,例如行式打印机假脱机守护进程就可能将其工作目录更改到它们的spool目录上。

(5)   关闭不再需要的文件描述符。可以使用open_max函数或者getrlimit函数获取最高文件描述符值,然后关闭直到该值的所有文件描述符。这样做可以避免守护进程从其父进程继承任何文件描述符。

(6)   某些守护进程将文件描述符0、1、2指向/dev/null。这样可以使得任何以恶搞试图读标准输入、写标准输出、写标准错误输出的程序不产生任何效果。由于守护进程是在后台运行的,因此登录会话的终止并不影响守护进程。如果其他用户在同一终端设备上登录,我们自然不希望在该终端上见到守护进程的输出,用户也不希望他们在终端上的输入被守护进程读取,因此上述措施是相当有用的。

如下程序可由想要初始化为守护进程的程序调用,在main函数中调用函数daemonize,然后使main进程进入休眠状态,通过ps –efj命名查看进程状态,可以发现守护进程init,其终端名为 ?。

[root@benxintuzi process]# cat init.c
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <syslog.h>
#include <fcntl.h>
#include <sys/resource.h> void daemonize(const char *cmd)
{
int i, fd0, fd1, fd2;
pid_t pid;
struct rlimit rl;
struct sigaction sa; /*
* * Clear file creation mask.
* */

umask(); /*
* * Get maximum number of file descriptors.
* */

if (getrlimit(RLIMIT_NOFILE, &rl) < )
{
printf("%s: can't get file limit\n", cmd);
return ;
} /*
* * Become a session leader to lose controlling TTY.
* */

if ((pid = fork()) < )
{
printf("%s: can't fork\n", cmd);
return ;
}
else if (pid != ) /* parent */
exit();
setsid(); /*
* * Ensure future opens won't allocate controlling TTYs.
* */

sa.sa_handler = SIG_IGN;
sigemptyset(&sa.sa_mask);
sa.sa_flags = ;
if (sigaction(SIGHUP, &sa, NULL) < )
{
printf("%s: can't ignore SIGHUP\n", cmd);
return ;
}
if ((pid = fork()) < )
{
printf("%s: can't fork\n", cmd);
return ;
}
else if (pid != ) /* parent */
exit(); /*
* * Change the current working directory to the root so
* * we won't prevent file systems from being unmounted.
* */

if (chdir("/") < )
{
printf("%s: can't change directory to \n", cmd);
} /*
* * Close all open file descriptors.
* */

if (rl.rlim_max == RLIM_INFINITY)
rl.rlim_max = ;
for (i = ; i < rl.rlim_max; i++)
close(i); /*
* * Attach file descriptors 0, 1, and 2 to /dev/null.
* */

fd0 = open("/dev/null", O_RDWR);
fd1 = dup();
fd2 = dup(); /*
* * Initialize the log file.
* */

openlog(cmd, LOG_CONS, LOG_DAEMON);
if (fd0 != || fd1 != || fd2 != ) {
syslog(LOG_ERR, "unexpected file descriptors %d %d %d",
fd0, fd1, fd2);
exit();
}
} int main(void)
{
daemonize("ps");
sleep();
return ;
} [root@benxintuzi process]# ./init
[root@benxintuzi process]# ps
-efj
UID PID PPID PGID SID C STIME TTY TIME CMD
root : ? :: /sbin/init
root : pts/ :: vim init.c
root 2237 1 2236 2236 0 19:45 ? 00:00:00 ./init
root : pts/ :: ps -efj

Linux 进程(二):进程关系及其守护进程的更多相关文章

  1. linux系统编程之进程(八):守护进程详解及创建,daemon()使用

    一,守护进程概述 Linux Daemon(守护进程)是运行在后台的一种特殊进程.它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.它不需要用户输入就能运行而且提供某种服务,不是对整个 ...

  2. (七) 一起学 Unix 环境高级编程(APUE) 之 进程关系 和 守护进程

    . . . . . 目录 (一) 一起学 Unix 环境高级编程 (APUE) 之 标准IO (二) 一起学 Unix 环境高级编程 (APUE) 之 文件 IO (三) 一起学 Unix 环境高级编 ...

  3. Linux 的进程组、会话、守护进程

    一.进程组ID 每个进程都属于一个进程组.每个进程组有一个领头进程.进程组是一个或多个进程的集合,通常它们与一组作业相关联,可以接受来自同一终端的各种信号.每个进程组都有唯一的进程组ID(整数,也可以 ...

  4. linux第1天 fork exec 守护进程

    概念方面 文件是对I/O设备的抽象表示.虚拟存储器是对主存和磁盘I/O设备的抽象表示.进程则是对处理器.主存和I/O设备的抽象表示 中断 早期是没有进程这个概念,当出现中断技术以后才出现进程这个概念 ...

  5. Linux系统编程(26)——守护进程

    Linux系统启动时会启动很多系统服务进程,比如inetd,这些系统服务进程没有控制终端,不能直接和用户交互.其它进程都是在用户登录或运行程序时创建,在运行结束或用户注销时终止,但系统服务进程不受用户 ...

  6. linux c语言学习笔记之守护进程

    哈尔滨理工大学软件工程专业08-7李万鹏原创作品,转载请标明出处 http://blog.csdn.net/woshixingaaa/archive/2010/06/06/5651095.aspx 守 ...

  7. Python之路(第三十七篇)并发编程:进程、multiprocess模块、创建进程方式、join()、守护进程

    一.在python程序中的进程操作 之前已经了解了很多进程相关的理论知识,了解进程是什么应该不再困难了,运行中的程序就是一个进程.所有的进程都是通过它的父进程来创建的.因此,运行起来的python程序 ...

  8. 浅析linux 下shell命令执行和守护进程

    执行shell脚本有以下几种方式 1.相对路径方式,需先cd到脚本路径下 [root@banking tmp]# cd /tmp [root@banking tmp]# ./ceshi.sh 脚本执行 ...

  9. Linux+Nginx+Asp.net Core及守护进程部署

    上篇<Docker基础入门及示例>文章介绍了Docker部署,以及相关.net core 的打包示例.这篇文章我将以oss.offical.site站点为例,主要介绍下在linux机器下完 ...

随机推荐

  1. 倍福TwinCAT(贝福Beckhoff)应用教程13.3 TwinCAT控制松下伺服 NC配合完整上位

    这是TwinCAT教程的最后一节,简单讲述了以C#为上位,通过ADS控制TwinCAT下位,实现完整控制两轴模组的功能.可以发现,在上位层已经没有了运动控制的代码,不管是要执行哪种运动,无非是把目标参 ...

  2. window onload 与 img onload事件

    window onload 与 img onload事件 通常,window.onload就是载入完dom之后运行的. 而img就是载入完图片完运行它的onload事件.依据img的src是否载入完毕 ...

  3. 微信小程序bindtap和catchtap区别

    bindtap可以产生冒泡事件 catchtap只自身触发事件,不会传递到父视图         文章来源:刘俊涛的博客 地址:http://www.cnblogs.com/lovebing 欢迎关注 ...

  4. 利用Json_encode解决中文问题

    利用Json_encode解决中文问题       public function return_json($data=array()){         echo json_encode($data ...

  5. Template design pattern application in android

    The template method allow separate the generic method from a detail context(class) via inheritance A ...

  6. 单用户模式下mount -o remount,rw / 有大用途

    我们的Linux系统在无法启动时候,通常需要进入单用户模式下进行修改一些配置文件,或调整一些参数方可.但是在进入单用户模式后,我们的/文件系统是只读模式,无法进行修改,那么这个时候我们就需要用到一条命 ...

  7. 去除前后空格,Oracle和SQLSERVER都适用。ltrim(rtrim(’ ‘))

    Oracle自带去除方法:trim(). 但是sql语法中没有直接去除两头空格的函数,但有ltrim()去除左空格rtrim()去除右空格. 合起来用就是sql的trim()函数,即select lt ...

  8. Android之——ContentResolver查询的三种方式

    转载请注明出处:http://blog.csdn.net/l1028386804/article/details/47785491 今天做到一个小项目.查询手机中短信的信息,当然得去系统暴露出来的数据 ...

  9. ModelSim高级使用进阶_1_do文件和批处理文件使用_Camp

    https://wenku.baidu.com/view/50fb251914791711cc7917fd.html https://wenku.baidu.com/view/73187dcefe47 ...

  10. Chap 2 Representing and Manipulating Information (CS:APP)

    -------------------------------------------------------------------------------------------------- A ...