一.进程组.会话.终端概念
1.1进程组
在Linux操作系统中,进程组(Process Group)是一组进程的集合。进程组内的每个进程都有一个相同的进程组ID(PGID)。进程组可以用于进行作业控制、信号传递和进程状态管理等操作。
每个进程组都有一个组长进程,其进程ID(PID)等于进程组ID(PGID)。通常,进程组的第一个进程会成为组长进程。
进程组主要有以下特点:
-
创建进程组:可以通过
setpgid()
系统调用将进程添加到指定的进程组或创建一个新的进程组。常见的方式是使用fork()
和exec()
函数,子进程可以通过setpgid(0, 0)
将自己加入到新进程组,或者使用setpgid(pid, pgid)
将指定进程加入到现有进程组。 -
进程组ID:进程组ID(PGID)是一个非负整数,用于唯一标识一个进程组。进程组ID由操作系统分配并在进程组创建时指定。
-
进程组的作用:进程组常用于作业控制和信号传递。作业控制通过对进程组的操作来管理一组相关联的进程,例如发送信号给整个进程组、暂停或恢复进程组中的所有进程等。
-
进程组的关系:进程组不存在嵌套关系,每个进程组都是扁平的,不包含其他进程组。然而,一个进程可以同时属于多个进程组,即多个进程组ID与其PID相等。这在进行作业控制和信号传递时可能有用。
总而言之,进程组是一组拥有相同进程组ID的进程的集合。它们提供了一种对进程进行组织和管理的机制,并在作业控制、信号传递和进程状态管理等方面发挥重要作用。
1.2父进程和子进程
当进程调用了 fork()
函数创建子进程时,子进程会成为父进程的副本,包括进程组的关系。在默认情况下,父子进程会属于同一个进程组,且父进程作为进程组的组长。具体来说:
-
在
fork()
函数调用后,子进程将会获得父进程的副本,包括进程组ID(PGID)。 -
父进程的进程组ID(PGID)不会改变,即父进程仍然是进程组的组长。
-
子进程的进程组ID(PGID)将与父进程相同。
这意味着,父进程和子进程将属于同一个进程组,且父进程仍然是进程组的组长。
需要注意的是,在某些特定的情况下,父进程可能会在 fork()
后修改子进程的进程组ID(PGID),或者子进程可能会通过调用相关的系统调用来修改自己的进程组ID(PGID)。
总结起来,当进程调用了 fork()
函数创建子进程时,默认情况下,父子进程将属于同一个进程组,且父进程仍然是进程组的组长。子进程将继承父进程的进程组ID(PGID)。尽管如此,父进程和子进程仍然可以通过相关的系统调用修改自己的进程组ID(PGID)。
1.3shell的管道
在 shell 中通过管道连接的应用程序,确实会创建一个进程组,并且第一个程序成为进程组的组长。
当在 shell 中使用管道 |
连接两个应用程序时,shell 会创建一个子进程来执行第一个应用程序,并将其设为进程组的组长。然后,shell 创建另一个子进程来执行第二个应用程序,并将其加入到与第一个应用程序相同的进程组中。这样,两个应用程序就属于同一个进程组,且第一个应用程序成为该进程组的组长。
此设计的目的是为了能够对整个管道进行作业控制和信号传递。作业控制允许用户对一组相关联的进程进行统一管理,而信号传递可以在需要的时候将信号发送给整个进程组。
需要注意的是,由于管道中的进程是通过管道进行输入和输出的,它们之间的通信是通过内核缓冲区进行的,而不是直接的进程间通信。
总结起来,在 shell 中通过管道连接的应用程序,这两个程序属于同一个进程组,第一个程序成为进程组的组长,这样可以方便进行作业控制和信号传递。
1.4shell执行一个程序
在 shell 中直接执行一个应用程序时,对于大部分进程来说,它们自身成为一个独立的进程组,因此进程组中只包含一个进程。
当在 shell 中执行一个应用程序时,shell 会创建一个子进程来执行该应用程序。这个子进程成为一个独立的进程组,进程组ID(PGID)和进程ID(PID)相同。这意味着该进程是进程组中唯一的进程,也是进程组的组长。
在这种情况下,进程组的目的可能不是为了作业控制或信号传递,而是为了在执行过程中将其与其他进程分隔开,以获得更好的隔离性和独立性。
需要注意的是,这只适用于大部分的情况。在某些特殊的情况下,应用程序可能会以其他方式创建自己的进程组,或者与其他进程共享进程组。这种情况是因为应用程序的特殊需求而进行的特定处理。
总结起来,当在 shell 中直接执行一个应用程序时,这个应用程序通常会成为一个独立的进程组,进程组中只包含一个进程。这种设计可以提供进程的隔离性和独立性。但需要注意的是,在特殊情况下,应用程序可能会以其他方式创建进程组或与其他进程共享进程组。