前台进程,后台进程,守护进程的对比
在前面我们已经了解了前台进程,后台进程,守护进程。
直接在终端中输入命令: 这是最常见的启动前台进程的方式。例如,在终端中输入 ./myprogram
就可以启动 myprogram
程序,它会作为前台进程运行。
在命令末尾添加 &
符号:这是最简单直接启动后台进程的方法。在终端中输入命令时,在命令的末尾添加 &
符号,然后按下回车键,程序就会在后台启动。例如:./myprogram &
守护进程创建启动方式我们在上一篇文章中已经详细介绍过了。
进程占用进程组的问题
默认启动一个进程时,这个进程本身就会单独占据一个进程组。
- 默认行为: 默认情况下,当我们启动一个程序时,操作系统会自动将该程序放入一个进程组中。
- 进程组组长: 进程组中的第一个进程(通常是启动的那个进程)会自动成为该进程组的组长。
举例说明:
假设我们在终端中输入 ./myprogram
命令或者./myprogram &
命令启动了一个名为 myprogram
的程序。
- 操作系统会自动创建一个新的进程组,并为该进程组分配一个唯一的PGID。
myprogram
程序会成为这个进程组的组长。- 这个进程组只包含
myprogram
这一个进程。
使用./myprogram
命令启动进程之后这个进程被放到了前台进程组,并且这个进程中创建的子进程也是在前台进程组中。
使用./myprogram
&命令启动进程之后这个进程被放到了后台进程组,并且这个进程中创建的子进程也是在后台进程组中。
下面一些情况下进程不会单独占据一个进程组
父进程创建子进程
- 当一个进程(父进程)使用
fork()
函数创建子进程时,子进程默认会加入到父进程所在的进程组。 - 这意味着父进程和子进程会共享同一个进程组ID(PGID)。
- 这种情况下,子进程不会单独占据一个进程组,而是与父进程共享同一个进程组。
进程加入已存在的进程组
- 一个进程可以使用
setpgid()
函数加入到一个已存在的进程组。 - 如果一个进程加入了其他进程已存在的进程组,那么它就不会单独占据一个进程组。
setpgid函数介绍
函数作用:改变进程的进程组
函数原型:int setpgid(pid_t pid, pid_t pgid);
参数介绍:
pid:要加入进程组的进程的进程 ID(PID)。如果 pid 为 0,则表示调用进程本身。
pgid:目标加入进程组 ID(PGID)。如果 pgid 为 0,则表示将进程设置为其 PID 所对应的进程组。若目标进程组 ID 为一个合法的进程组 ID,则调用进程将加入该进程组。
返回值介绍:
- 成功时返回
0
。 - 失败时返回
-1
,并设置errno
为相应的错误码。
常见使用情景:
- 父子进程的关系:子进程在默认情况下会继承父进程的进程组 ID。如果父进程的进程组 ID 是
pgid
,那么子进程也会属于同一个进程组。通过setpgid()
,可以显式地改变进程组的归属。
比如可以通过setpgid()来让子进程单独占据一个进程组
- 进程组和信号:进程组之间可以通过信号来进行通信,
setpgid()
允许你根据需要来控制进程的分组。你可以将多个进程组织到一个进程组中,然后使用信号(如kill()
)向整个进程组发送信号。
注意点:
进程使用setsid() 函数用于创建一个新的会话,要求调用进程不能是进程组的组长。进程调用setpgid()
函数的时候,并没有要求调用进程不能是进程组的组长。注意不要混淆。
进程使用setpgid() 函数之后只是将进程所属的进程组变了,它仍然是原来的父进程的子进程。setpgid()
函数只影响进程所属的进程组,而不改变进程的父子关系。
补充说明(先看)
由于进程改变所属的进程组这个操作其实比较少见,所以这里对于setpgid函数的详细使用例子我也没给出来,只是介绍了函数的用法,感兴趣的读者可以自行学习。另外关于进程组相关的函数接口还有一个比较重要,但是本文没有提到的是 pid_t getpgid(pid_t pid); 这个函数接口用于获取指定进程的进程组ID,使用方法很简单,这里我也不多赘述。