文章目录
1.终端与 bash 进程
查看 bash 进程的命令如下:
ps -ef | grep bash
每连接一个虚拟终端到 Ubuntu 操作系统,就会出现一个 bash 进程。
bash (Bourne Again Shell) 是 shell 的一种,bash 是多数 Linux 系统默认的 shell。bash 是一个可执行程序,主要作用就是把用户输入的命令翻译给操作系统(命令解释器)。
2.终端上的开启进程
创建一个 test.c
文件,其内容如下:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* const* argv)
{
printf("你好,世界!\n");
for (;;)
{
sleep(1);
printf("休息1秒\n");
}
printf("程序退出,再见!\n");
return 0;
}
编译 test.c
文件的命令如下:
gcc -o test test.c
运行可执行文件 test
的命令如下:
./test
从上图可以看出,test 进程是 bash 进程的子进程。
对于那些由用户执行命令所启动的进程都是与终端相关的,当把终端关闭,该终端中的所有进程也都会自动关闭。
在 Linux 中,进程之间的关系如下:
3.进程组与会话
在 Linux 中,每一个进程还属于一个进程组,一个进程组由一个或者多个进程组成。通常,它们联合起来作业,可以接受同一个终端下的各种信号。并且每一个进程组都有一个唯一的进程组ID(在 Linux 中叫做 PGID),进程组中有一个组长进程,组长进程的标识是它的进程ID和组ID相同。一般,一个进程组中第一个创建的进程就是组长进程。一个进程组中只要还有一个进程存在,那么这个进程组就存在。我们可以调用系统函数来创建进程组、加入进程组。
会话(session)是一个或者多个进程组的集合,一般情况下,只要不进行特殊的系统函数调用,一个 bash 上边运行的所有程序都属于一个会话。会话有一个 session leader,通常这个 bash 就是 session leader。
指定输出字段与格式以显示进程属性的命令如下:
ps -eo pid,ppid,pgid,sid,tty,comm | grep -E 'bash|PID|test'
关于 ps
命令各个选项和参数的含义:https://wker.com/linux-command/ps.html
如果我们要把终端断开的话,系统就会发送 SIGHUP 信号(即终端断开信号)给 session leader(即这个 bash 进程),bash 进程收到 SIGHUP 信号后,bash 就会把这个 SIGHUP 信号发送给 session 里边的所有进程,收到这个 SIGHUP 信号的进程的缺省动作就是退出。
4.strace 工具的使用
strace 工具是 Linux 下调试分析诊断工具,可以跟踪程序执行时进程的系统调用以及所收到的信号。
跟踪 test 进程的命令如下:
sudo strace -e trace=signal -p 1238
跟踪 bash 进程的命令如下:
sudo strace -e trace=signal -p 1164
我们直接关闭 test 程序所在的终端,上述两条命令的输出结果如下:
5.终端关闭时如何让进程不退出
5.1 test 进程拦截 SIGHUP 信号
5.1.1 SIG_IGN 标志
test 进程收到 SIGHUP 信号之后,告诉操作系统“请不要把我杀死”。
修改 test.c
的内容如下:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int main(int argc, char* const* argv)
{
printf("你好,世界!\n");
// SIG_IGN标志:要求忽略这个信号,请求操作系统不要用缺省的处理方式来对待我,即不要把我杀掉
signal(SIGHUP, SIG_IGN);
for (;;)
{
sleep(1);
printf("休息1秒\n");
}
printf("程序退出,再见!\n");
return 0;
}
编译 test.c
:
gcc -o test test.c
运行 test
:
./test
查看进程:
ps -eo pid,ppid,pgid,sid,tty,comm | grep -E 'bash|PID|test'
我们直接关闭 test
程序所在的终端,从下图可以看到,bash 进程被杀死了,test 进程仍然存在:
5.1.2 nohup 命令
使用 nohup
命令启动的进程能够忽略掉 SIGHUP
信号,该命令会把屏幕输出重新定位到当前目录的 nohup.out
文件中。
test.c
的内容如下:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* const* argv)
{
printf("你好,世界!\n");
for (;;)
{
sleep(1);
printf("休息1秒\n");
}
printf("程序退出,再见!\n");
return 0;
}
编译 test.c
:
gcc -o test test.c
运行 test
:
nohup ./test
查看进程:
ps -eo pid,ppid,pgid,sid,tty,comm | grep -E 'bash|PID|test'
我们直接关闭 test
程序所在的终端,查看进程:
5.2 让 test 进程和 bash 进程不在同一个 session 里
5.2.1 setsid() 函数
需要注意的是,进程组组长调用 setsid()
函数是无效的。
修改 test.c
的内容如下:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* const* argv)
{
pid_t pid;
printf("你好,世界!\n");
pid = fork(); // 系统函数,用来创建新进程。子进程会从fork()调用之后开始执行。
if (pid < 0)
{
printf("fork()进程出错!\n");
}
else if (pid == 0)
{
printf("子进程开始执行!\n");
setsid(); // 新建立一个不同的session
for (;;)
{
sleep(1);
printf("子进程休息1秒\n");
}
return 0;
}
else
{
printf("父进程开始执行!\n");
for (;;)
{
sleep(1);
printf("父进程休息1秒\n");
}
return 0;
}
printf("程序退出,再见!\n");
return 0;
}
编译 test.c
:
gcc -o test test.c
运行 test
:
./test
查看进程:
ps -eo pid,ppid,pgid,sid,tty,comm | grep -E 'bash|PID|test'
我们直接关闭 test
程序所在的终端,查看进程:
5.2.2 setsid 命令
使用 setsid
命令启动一个进程能够使启动的进程在一个新的 session 中,这样的话,终端关闭时该进程就不会退出。
test.c
的内容如下:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* const* argv)
{
printf("你好,世界!\n");
for (;;)
{
sleep(1);
printf("休息1秒\n");
}
printf("程序退出,再见!\n");
return 0;
}
编译 test.c
:
gcc -o test test.c
运行 test
:
setsid ./test
查看进程:
ps -eo pid,ppid,pgid,sid,tty,comm | grep -E 'bash|PID|test'
我们直接关闭 test
程序所在的终端,查看进程:
6.后台运行
后台运行的命令如下:
./test &
后台执行这个程序的同时,终端能够干其他事情;如果不用后台执行,那么终端就只能等这个程序完成后才能继续执行其他的操作。
切换到前台的命令如下:
fg