会话
在Linux中,会话(session)是一组相关联的进程的集合,它们可以接收相同的信号,并且通常由一个领导进程来控制。会话中的所有进程都共享同一个控制终端(controlling terminal),这个终端通常是会话的首个进程(也就是会话期领导者进程)打开的终端。
当用户登录到系统时,通常会启动一个新的会话。会话期领导者进程通常是登录shell,它负责启动其他进程并创建新的进程组和会话。如果会话期领导者进程退出,那么整个会话将结束并且该会话中的所有进程都将收到 SIGHUP 信号(默认行为是终止进程)。
会话还有一个与之相关的概念:作业(job)。作业是指在同一会话中的一组相关进程。在交互式shell中,通常可以使用前台作业(foreground job)和后台作业(background job)来管理正在运行的进程。
proc1|proc2 &
这个命令是Linux下的一个管道(pipe)和后台运行(background)的组合操作。
将两个进程proc1和proc2通过管道连接起来,使得proc1的输出可以传递给proc2进行处理。在命令结尾处添加“&”符号表示让整个命令在后台运行,即不阻塞当前shell终端,并且可以同时在该终端中输入其他命令。
具体来说,当执行该命令时,shell会首先创建一个新的子进程来执行proc1和proc2的管道操作,然后立即返回到shell提示符下等待输入新命令。由于该命令在后台运行,因此控制权立即被交还给用户,而不必等待命令执行完毕。至于管道操作的结果,则会显示在标准输出中或者被重定向到其他文件中,具体取决于命令的具体形式。
需要注意的是,如果在后台运行的命令产生了输出,那么它们的输出可能会与用户输入的其他命令的输出混合在一起,因此需要小心处理。此外,也可以使用jobs命令查看当前正在后台运行的任务的状态,并且可以使用fg或bg命令将前台作业切换到后台或反之。
$proc3|proc4|proc5
这个命令看起来是在使用三个进程:proc3、proc4和proc5。
这个命令可能使用了竖杠符号“|”,在基于Unix的操作系统中,“|”符号通常表示管道。管道允许将一个进程的输出作为另一个进程的输入。
根据这个假设,这个命令将 proc3 的输出通过管道传递给 proc4,然后将 proc4 的输出通过管道传递给 proc5。这可以用于链接命令并执行更复杂的操作。
会话的一些特征
在Linux系统中,会话通常指一个用户与系统交互的过程。Linux的会话具有以下一些特征:
-
登录:会话开始于用户登录系统之后,在终端或者远程终端运行shell程序。
-
进程组和作业控制:在一个会话内,多个进程可以组成进程组,而进程组又可以被分为不同的作业。Linux提供了作业控制机制,允许用户对作业进行管理。
-
控制终端:一个会话通常与一个控制终端相关联。控制终端是用户输入和输出的接口,也是信号传递的媒介。
-
会话和进程的关系:在Linux中,每个进程都属于一个会话,而会话又包含了多个进程。当父进程创建一个子进程时,子进程会自动成为父进程所在会话的成员。
-
信号:在Linux系统中,进程间通信主要依靠信号。在一个会话中,进程可以向会话内其他进程发送信号,以实现相应的交互。
-
生命周期:一个会话的生命周期从用户登录开始,到用户退出系统结束。在此期间会话中的进程可以执行各种操作,并且可能会产生不同的事件和状态。
守护进程
守护进程(daemon)是在后台运行的一种特殊类型的进程,它们通常在系统启动时启动,并在系统关闭时终止。守护进程不与任何控制终端相连,因此它们不能从终端接收输入或向终端输出数据。
守护进程通常被用来执行一些需要在后台长时间运行的任务,例如网络服务、系统监控、日志记录等。它们可以周期性地执行一些任务,或者根据需要启动和停止其他进程。
一个好的守护进程应该有以下几个特点:
能够在后台默默地工作,不会占用用户的交互式控制终端。
能够自我管理并保证稳定性,能够处理各种异常情况,如资源不足、网络中断等。
能够记录所有重要事件以及错误信息,便于管理员进行故障排除和系统调试。
能够按需启动、停止或重启其他进程,实现某些功能,如更新配置文件、重新加载模块等。
总之,守护进程是Linux/Unix操作系统中非常重要的一部分,它们可以为系统提供各种服务和支持,保持系统安全、稳定和高效运行。
要创建守护进程,一般需要按照以下步骤进行:
-
创建一个子进程,并使父进程退出。这可以通过调用fork()系统调用来实现。
-
子进程调用setsid()系统调用创建一个新的会话并成为新会话的领导进程,从而脱离当前控制终端和进程组的关系。
-
将工作目录切换到根目录,以使守护进程不再依赖于任何文件系统。
-
关闭所有文件描述符,包括标准输入、标准输出和标准错误输出,避免干扰其他进程或收到不必要的信息。
-
重新打开标准输入、标准输出和标准错误输出,将它们指向/dev/null设备,以确保所有输出都被丢弃,不会对其他程序产生影响。
-
在守护进程中执行需要的任务,例如接受网络请求、执行系统监控等。
下面是一个简单的创建守护进程的代码示例:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
int main() {
pid_t pid;
int fd;
// 1. 创建子进程并退出父进程
if ((pid = fork()) < 0) {
perror("fork error");
exit(1);
} else if (pid != 0) {
exit(0);
}
// 2. 子进程创建新会话并成为领导进程
if (setsid() < 0) {
perror("setsid error");
exit(1);
}
// 3. 切换工作目录到根目录
if (chdir("/") < 0) {
perror("chdir error");
exit(1);
}
// 4. 关闭所有文件描述符
close(STDIN_FILENO);
close(STDOUT_FILENO);
close(STDERR_FILENO);
// 5. 重新打开标准输入输出并重定向到/dev/null
fd = open("/dev/null", O_RDWR, 0);
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
if (fd > STDERR_FILENO) {
close(fd);
}
// 6. 在守护进程中执行任务
while (1) {
// do something...
}
return 0;
}
这个例子中,我们创建了一个子进程,并调用setsid()系统调用将其设置为新会话的领导进程。然后,我们切换当前工作目录到根目录以避免依赖于任何文件系统。接着,我们关闭所有文件描述符,包括标准输入、标准输出和标准错误输出,避免干扰其他进程或收到不必要的信息。最后,我们重新打开标准输入、标准输出和标准错误输出,并将它们重定向到/dev/null设备,以确保所有输出都被丢弃,不会对其他程序产生影响。最后,在守护进程中执行需要的任务,例如接受网络请求、执行系统监控等。