前言
本篇文章我们来讲解会话和进程的概念,会话大家可能比较少见,他的英文名称叫session。
一、会话的概念
在Linux中,会话(Session)是指用户与操作系统交互的一段时间。Linux下的会话概念是基于终端(Terminal)的,而终端可以是物理终端、虚拟终端(例如TTY)或远程连接(如SSH)。
以下是Linux中会话的几个关键概念:
1.控制终端:
在一个会话中,通常会存在一个控制终端,为用户提供输入和输出的交互界面。用户通过控制终端与操作系统进行交互,包括输入命令、运行程序和查看输出。
2.终端会话:
一个用户可以在控制终端上启动一个或多个终端会话。每个终端会话都是一个独立的进程组,包含一个前台进程组和零个或多个后台进程组。
3.前台进程组:
前台进程组是当前用户正在交互的进程组。当用户从控制终端输入命令时,命令将发送给前台进程组的前台进程进行处理。只有一个进程组可以处于前台状态。
4.后台进程组:
后台进程组是在终端会话中运行的进程组,但没有用户输入交互。用户可以将进程从前台切换到后台,使其在后台运行而不阻塞终端。
5.会话管理程序:
Linux通过会话管理程序(session manager)来管理终端会话。会话管理程序负责创建和管理会话,设置控制终端,并在会话结束时清理资源。
6.会话注销和断开:
当用户注销或断开与终端的连接时,会话通常会终止。会话终止会导致终端上的所有进程(前台和后台)收到相应的信号来进行清理和终止。
会话在Linux中是重要的概念,它提供了用户与操作系统之间的交互环境,并管理用户的进程组。了解和理解Linux中的会话概念对于正确地管理和控制用户的交互行为是至关重要的。
二、会话和终端的区别
会话(Session)和终端(Terminal)是两个相关但不完全相同的概念。
会话是指用户与操作系统或应用程序之间的交互过程,涉及到用户的身份验证、持久性连接、状态保持等方面。在会话中,用户可以通过终端或其他交互方式与系统进行通信。
终端是一个提供用户与计算机交互的设备或界面,用户可以通过终端输入命令、查看输出和与系统进行交互。终端可以是物理设备(如物理终端设备、控制台)或虚拟设备(如虚拟终端、SSH连接)。终端提供了用户与系统之间的输入和输出通道。
下面是它们之间的区别:
1.概念层面:
会话是指用户与操作系统或应用程序之间的交互过程,包括用户的身份验证、持久性连接和状态保持等。
终端是用户与计算机交互的设备或界面,它提供了用户输入和输出的通道。
2.关联:
会话和终端通常是相关的。一个用户可以通过终端设备或虚拟终端启动一个会话,并在会话中与系统进行交互。
一个终端可以有多个会话,例如在同一终端设备上打开多个虚拟终端会话。
3.功能:
会话涉及用户的身份验证、状态保持和持久性连接等功能,用于管理用户与系统之间的交互。
终端提供了用户输入命令、查看输出和与系统进行交互的功能。
4.物理性质:
会话是一个抽象的概念,表示用户与系统的交互过程,不涉及具体的物理设备。
终端可以是物理设备(如键盘、显示器)或虚拟设备(如虚拟终端、SSH连接)。
当命令行shell执行新的命令创建出新的进程时:
使用&创建新的进程,新建的进程是后台进程,自己依然是前台进程。
不使用&创建新的进程,新建的进程是前台进程,自己被设置为后台进程。
三、终端进程组标识
在Linux中,每个终端会话都有一个唯一的进程组标识符(Process Group ID,PGID)。终端进程组标识是为了实现作业控制和进程管理而引入的。
每个终端会话中的进程都隶属于一个进程组。当用户在终端上启动一个新的进程时,默认情况下,该进程会被分配到同一终端会话的进程组中。
终端进程组标识具有以下特点:
1.会话首进程的进程组标识:
在每个会话中,会有一个会话首进程(session leader)创建该会话,会话首进程的进程组标识与会话ID(Session ID,SID)相同。
2.前台进程组:
在终端会话中,只能有一个前台进程组(foreground process group)。该前台进程组通常接收来自终端的用户输入,并将输出发送到终端。用户在终端上输入的命令会被发送给前台进程组中的前台进程。
3.后台进程组:
终端会话可以同时包含多个后台进程组(background process group)。后台进程组是在终端会话中运行的进程组,但不接受用户输入。后台进程组可以在终端会话运行而不阻塞终端。
通过使用终端进程组标识,系统可以实现对作业控制的管理,包括在前台或后台运行进程组、切换前后台进程组、发送信号等。
要查看当前终端进程组标识,您可以使用echo $$命令。该命令会输出当前Shell进程的进程ID(PID),该Shell进程所在的进程组ID就是终端进程组标识。
四、创建会话
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { pid_t pid; // 创建一个子进程 pid = fork(); if (pid < 0) { fprintf(stderr, "无法创建子进程\n"); return 1; } else if (pid == 0) { // 子进程 // 创建一个新的会话 if (setsid() < 0) { fprintf(stderr, "无法创建新会话\n"); return 1; } // 关闭标准输入、输出和错误输出 close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); // 从这里开始,子进程就可以执行会话中的任务 // 示例:打开一个日志文件并写入日志 FILE* logFile = fopen("/var/log/mylog.txt", "w"); if (logFile == NULL) { fprintf(stderr, "无法打开日志文件\n"); return 1; } fprintf(logFile, "会话已启动\n"); fprintf(logFile, "执行一些任务\n"); // 关闭日志文件 fclose(logFile); // 子进程完成任务后退出 exit(0); } else { // 父进程 // 这里可以选择等待子进程完成或继续执行其他任务 printf("子进程的PID:%d\n", pid); } return 0; }
这个示例程序创建了一个子进程,子进程通过调用setsid()函数创建一个新的会话,然后关闭标准输入、输出和错误输出,开始执行会话中的任务(这里仅为示例,可以根据具体需求进行修改)。父进程可以选择等待子进程完成或继续执行其他任务。
总结
本篇文章就讲解到这里。