Unix/Linux编程:SIGHUP信号

article/2025/11/10 5:08:06

当会话首进程打开了一个控制终端之后它同时也成为了该终端的控制进程;当一个控制进程失去其终端连接后,内核会向其发送一个SIGHUP信号来通知它这一事实(还会发送一个SIGCONT信号以确保当该进程之前被一个信号停止时重新开始该进程)。一般来讲,这种情况可能会在下面两个场景中出现:

  • 当终端驱动器检测到连接断开后,表明调制解调器会在终端行上信号丢失
  • 当工作站上的终端窗口被关闭时。发生这种情况是因为最近打开的与终端窗口关联的伪终端的主侧的文件描述符被关闭了

SIGHUP信号的默认处理方式是终止进程。如果控制进程处理了或者忽略了这个信号,那么后继尝试从终端中读取数据的请求就会返回文件结束的错误。

SUSv3 声称如果终端断开发生的同时还满足调用 read()时抛出 EIO 错误的条件的话,那么调用 read()既有可能返回文件结束,也有可能返回 EIO 错误。可移植的程序必须要处理好这两种情况

向控制进程发送SIGHUP信号会引起一种链式反应,从而导致SIGHUP信号发送给很多其他进程。这个过程可能会以下面两种方式发生:

  • 控制进程是一个shell。shell建立了一个SIGHUP信号的处理器,这样在进程终止之前,它能够将SIGHUP信号发送给由它所创建的各个任务。在默认情况下,这个信号会终止那些任务,但如果它们捕获了这个信号,就能知道shell进程已经终止了
  • 在终止终端的控制进程时,内核会解除会话中所有进程与该控制终端之间的关联关系以及控制终端与该会话的关联关系(因此另一个会话首进程可以请求该终端成为控制终端了),并且通过向该终端的前台进程组的成员发送 SIGHUP 信号来通知它们控制终端的丢失

SIGHUP 信号也可以用作他用:

  • 当一个进程组成为孤儿进程组时会生成 SIGHUP 信号。
  • 此外,手工发送 SIGHUP 信号通常用来触发 daemon 进程重新初始化自身
    或重新读取其配置文件。(根据定义,daemon 进程没有控制终端,因此无法从内核接收 SIGHUP信号。)

在shell中处理SIGHUP信号

在登录会话中,shell通常是终端的控制进程。大多数shell程序在交互式运行时会为SIGHUP信号建立一个处理器。这个处理器会处理shell,但在终止之前会向由shell创建的各个进程组(包括前台和后台进程组)发送一个 SIGHUP 信号。(在SIGHUP信号之后可能会发送一个SIGCONT信号,这依赖于shell本身以及任务当前是否处于停止状态)。至于这些组中的进程如何响应SIGHUP信号则需要根据应用程序的具体需求,如果不采取特殊的动作,那么默认情况下会终止进程。

  • 一些任务控制shell在正常退出(如登出或在 shell 窗口中接下 Control-D)时也会发送SIGHUP 信号来停止后台任务。bash 和 Korn shell 都采取了这种处理方式
  • nohup(1)命令可以用来使一个命令对SIGHUP信号免疫------即执行命令时将SIGHUP信号的处理设置为 SIG_IGN。bash 内置的命令 disown 提供了类似的功能,它从 shell 的任务列表中删除一个任务,这样在 shell 终止时就不会向该任务发送 SIGHUP 信号了。

下面演示了 shell 接收 SIGHUP 信号并向其创建的任务发送 SIGHUP 信号的过
程。

  • 这个程序的主要任务是创建一个子进程,然后让父进程和子进程暂停执行以捕获 SIGHUP信号并在收到该信号时打印一条消息。
  • 如果在执行程序时使用了一个可选的命令行参数(它可以是任意字符串),那么子进程会将其自身放置在一个不同的进程组中(在同一个会话中)。
  • 这个
    功能对于说明 shell 不会向不是由它创建的进程组发送 SIGHUP 信号,即使该进程组与 shell 位于同一个会话中来讲是非常有用的。(由于程序中最后一个 for 循环是一个无限循环,因此这个程序使用了 alarm()设置一个定时器来发送 SIGALRM 信号。如果一个进程没有终止的话,那么当它接收到 SIGALRM 信号而不做处理时会导致进程终止。
static void handler(int sig)
{
}
int main(int argc, char *argv[])
{pid_t childPid;struct sigaction sa;setbuf(stdout, NULL);       /* Make stdout unbuffered */sigemptyset(&sa.sa_mask);sa.sa_flags = 0;sa.sa_handler = handler;if (sigaction(SIGHUP, &sa, NULL) == -1){perror("sigaction");exit(1);}childPid = fork();if (childPid == -1){perror("fork");exit(1);}if (childPid == 0 && argc > 1)if (setpgid(0, 0) == -1)        /* Move to new process group */{perror("setgid");exit(1);}  printf("PID=%ld; PPID=%ld; PGID=%ld; SID=%ld\n", (long) getpid(),(long) getppid(), (long) getpgrp(), (long) getsid(0));alarm(60);                  /* An unhandled SIGALRM ensures this processwill die if nothing else terminates it */for (;;) {                  /* Wait for signals */pause();printf("%ld: caught SIGHUP\n", (long) getpid());}
}

运行:
在这里插入图片描述
第一个命令会导致创建两个进程,这两个进程属于由 shell 创建的进程组。第二个命令创建了一个子进程,子进程将自身放置在了一个不同的进程组中。

当查看 samegroup.log 时会发现其中包含了下面的输出,表明两个进程组的成员都收到了shell 发送的信号
在这里插入图片描述
当查看 diffgroup.log 时会发现下面的输出,表明 shell 在收到 SIGHUP 时不会向不是由它创建的进程组发送信号。
在这里插入图片描述

SIGHUP 和控制进程的终止

如果因为终端断开引起的向控制进程发送的 SIGHUP 信号会导致控制进程终止,那么
SIGHUP 信号会被发送给终端的前台进程组中的所有成员(见 25.2 节)。这个行为是控制进程终止的结果,而不是专门与 SIGHUP 信号关联的行为。如果控制进程出于任何原因终止,那么前台进程组就会收到 SIGHUP 信号。

在 Linux 上,SIGHUP 信号后面会跟着一个 SIGCONT 信号以确保在进程组之前被一个信号停止的情况下恢复该进程组。但 SUSv3 并没有指定这种行为,并且在这种情况下大多数其他UNIX 实现不会发送 SIGCONT 信号

下面程序演示了控制进程的终止导致向终端的前台进程组的所有成员发送 SIGHUP
信号。这个程序为每个命令行参数都创建了一个子进程。如果相应的命令行参数是 d,那么子进程会将自身放置在自己的(不同的)进程组中;否则的话子进程加入到父进程所在的进程组中。为确保它们能够在进程终止事件不发生的情况下正常终止,父进程和子进程都调用了 alarm()设置一个定时器以在 60 秒之后发送一个SIGALRM 信号。最后所有进程(包括父进程)打印出了它们的进程 ID 和进程组 ID⑥,接着循环等待信号的到达。当发出信号之后,处理器会打印出进程的进程 ID 和信号数值

static void             /* Handler for SIGHUP */
handler(int sig)
{printf("PID %ld: caught signal %2d (%s)\n", (long) getpid(),sig, strsignal(sig));/* UNSAFE (see Section 21.1.2) */
}
int
main(int argc, char *argv[])
{pid_t parentPid, childPid;int j;struct sigaction sa;if (argc < 2 || strcmp(argv[1], "--help") == 0)usageErr("%s {d|s}... [ > sig.log 2>&1 ]\n", argv[0]);setbuf(stdout, NULL);               /* Make stdout unbuffered */parentPid = getpid();printf("PID of parent process is:       %ld\n", (long) parentPid);printf("Foreground process group ID is: %ld\n",(long) tcgetpgrp(STDIN_FILENO));for (j = 1; j < argc; j++) {        /* Create child processes */childPid = fork();if (childPid == -1){perror("fork");exit(1);}if (childPid == 0) {            /* If child... */if (argv[j][0] == 'd')      /* 'd' --> to different pgrp */if (setpgid(0, 0) == -1){perror("setpgid");exit(1);}sigemptyset(&sa.sa_mask);sa.sa_flags = 0;sa.sa_handler = handler;if (sigaction(SIGHUP, &sa, NULL) == -1){perror("sigaction");exit(1);}break;                      /* Child exits loop */}}/* All processes fall through to here */alarm(60);      /* Ensure each process eventually terminates */printf("PID=%ld PGID=%ld\n", (long) getpid(), (long) getpgrp());for (;;)pause();        /* Wait for signals */
}

打开一个终端,执行下面命令:

exec ./disc_SIGHUP d s s > sig.log  2>&1

exec命令是一个shell内置命令,它会导致shell执行一个exec()来使用指定的程序取代自己。由于shell是终端的控制进程,因此现在这个程序已经成为了控制进程并且在终端窗口被关闭时会收到SIGHUP 信号。在关闭终端窗口之后,在sig.log 文件中会看到下面的输出。
在这里插入图片描述
关闭终端窗口会导致SIGHUP信号被发送给控制进程。(父进程),进而导致该进程的终止。从上面可以看出,两个子进程与父进程位于同一个进程组中(终端的前台进程组),它们都收到了 SIGHUP 信号,但位于另一个进程组(后台)中的子进程并没有收到这个信号

总结

SIGHUP 信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联. 系统对SIGHUP信号的默认处理是终止收到该信号的进程。所以若程序中没有捕捉该信号,当收到该信号时,进程就会退出。
  
SIGHUP会在以下3种情况下被发送给相应的进程:
  1、终端关闭时,该信号被发送到session首进程以及作为job提交的进程(即用 & 符号提交的进程);
  2、session首进程退出时,该信号被发送到该session中的前台进程组中的每一个进程;
   3、若父进程退出导致进程组成为孤儿进程组,且该进程组中有进程处于停止状态(收到SIGSTOP或SIGTSTP信号),该信号会被发送到该进程组中的每一个进程。
  
  例如:在我们登录Linux时,系统会分配给登录用户一个终端(Session)。在这个终端运行的所有程序,包括前台进程组和后台进程组,一般都属于这个 Session。当用户退出Linux登录时,前台进程组和后台有对终端输出的进程将会收到SIGHUP信号。这个信号的默认操作为终止进程,因此前台进 程组和后台有终端输出的进程就会中止。

此外,对于与终端脱离关系的守护进程,这个信号用于通知它重新读取配置文件。 比如xinetd超级服务程序。
  当xinetd程序在接收到SIGHUP信号之后调用hard_reconfig函数,它将循环读取/etc/xinetd.d/目录下的每个子配置文件,并检测其变化。如果某个正在运行的子服务的配置文件被修改以停止服务,则xinetd主进程讲给该子服务进程发送SIGTERM信号来结束它。如果某个子服务的配置文件被修改以开启服务,则xinetd将创建新的socket并将其绑定到该服务对应的端口上。


http://chatgpt.dhexx.cn/article/NeNSIW7Z.shtml

相关文章

Linux(程序设计):59---SIGHUP、SIGPIPE、SIGURG信号处理(附SIGURG信号处理普通数据与外带数据案例)

本文介绍3个与网络编程有关的3个信号 一、SIGHUP信号处理 信号产生的情景&#xff1a; 1.如果终端接口检测到一个连接断开&#xff0c;则将此信号送给与该终端相关的控制进程&#xff08;会话首进程&#xff09; 此信号被送给session结构中s_leader字段所指向的进程。仅当终端…

图解sql server 命令行工具sqlcmd的使用

一 操作实例 安装了sql server后此工具已经有了&#xff1b; 以sa登录&#xff1b; 打个命令&#xff0c;没反映&#xff1b; 可执行操作系统命令&#xff1b;加上!!即可&#xff1b; 查看帮助&#xff1b; 再打命令&#xff0c;也没反映&#xff1b;查询结果不显示&#xff1…

在ubuntu中使用命令行工具sqlcmd连接sql server数据库

步骤&#xff1a; 首先安装sqlcmd工具 注意&#xff1a;如果你连接的用户是 root用户&#xff0c;命令中带有 sudo 的&#xff0c;sudo都可以酌情去掉&#xff0c;不行再加上 1 安装curl软件包&#xff1a; sudo apt install curl 2 导入公共存储库 GPG 密钥 curl https:…

ssms,新建查询设置字体_如何在SSMS查询编辑器中使用SQLCMD命令

ssms,新建查询设置字体 SQLCMD Mode allows creating, testing, executing SQLCMD commands or scripts in SQL Server Management Studio directly in the query editor. This option is available since SQL Server 2005. SQLCMD模式允许直接在查询编辑器中在SQL Server Man…

使用SQLCMD在SQLServer执行多个脚本

使用SQLCMD在SQLServer执行多个脚本 概述&#xff1a; 作为DBA&#xff0c;经常要用开发人员提供的SQL脚本来更新正式数据库&#xff0c;但是一个比较合理的开发流程&#xff0c;当提交脚本给DBA执行的时候&#xff0c;可能已经有几百个sql文件&#xff0c;并且有执行顺序&…

sqlcmd命令执行大的SQL脚本

前言 这两天从服务器导出一个150多MB的脚本文件&#xff0c;导入到我本地的SQLServer数据库中时&#xff0c;直接打开执行提示内存不足的错误&#xff0c;于是google搜索发现微软针对此类情况有不少命令行工具&#xff0c;其中有一款sqlcmd 实用工具&#xff0c;官方文档地址为…

sql安装弹出sqlcmd_使用SQL Server命令行(sqlcmd)

sql安装弹出sqlcmd 介绍 (Introduction) Sqlcmd allows executing queries, T-SQL sentences and SQL Server scripts using the command line. Sqlcmd允许使用命令行执行查询&#xff0c;T-SQL语句和SQL Server脚本。 In the previous article How to work with the command…

SQL Server Management Studio 查询中使用 SQLCMD 模式

从 SQL Server 2005 开始&#xff0c;可以在 SQL Server Management Studio 查询编辑器中使用 SQLCMD 模式执行 TSQL。要在查询编辑器中编写或编辑 SQLCMD 脚本&#xff0c;需要启用 SQLCMD 模式。默认情况下&#xff0c;此模式是关闭的。 若要启用 SQLCMD 模式&#xff0c;请单…

sqlcmd去掉表头和X行受影响

使用sql语句导出到文件 sqlcmd -d test01 -U test01 -P test01 -S testdb.database.windows.net -Q "select * from test01" -o "test01.csv" -s "," -W -h-1 -k1 -d:数据库名 -U 用户名 -P 密码 -S 服务器名 -Q 执行查询后退出 -o 输出…

sqlcmd 命令

SQLCMD的介绍 文章转载自&#xff1a;http://blog.sina.com.cn/s/blog_3eec0ced0100mhm2.html 最近经常用到超过80M *.sql文件的导入问题。 上网找了一下&#xff0c;发现超过80M的文件是不能在查询分析器中执行的。 找了些解决方案&#xff0c;个人感觉最简单的方法就是这个…

cmd执行服务器sql文件命令行,Dos命令提示符下 - 用sqlcmd执行*.sql语句

Dos命令提示符下 - 用sqlcmd执行*.sql语句 1)在Dos命令下执行sqlcmd命令(当然事先需要将sqlcmd增加到环境变量中去), 2)下面白色部分替换为服务器名或计算机名即可 sqlcmd -S (local) -U sa -P 123 -d testdb -i C:\temp\data.sql 参数说明:-S 服务器地址 -U 用户名 -P 密码…

sql安装弹出sqlcmd_SQL Server中SQLCMD实用工具概述

sql安装弹出sqlcmd This article is aimed at helping you understand the sqlcmd utility. Of course, this is a tool most of us have used it at several occasions given that the tool has been around for a decade now. However, to be a solid starting point, this a…

SQLServer知识:sqlcmd用法笔记

今天给大家介绍sqlcmd用法笔记&#xff0c;希望对大家能有所帮助&#xff01; 1、介绍 sqlcmd是一个 Microsoft Win32 命令提示实用工具&#xff0c;可以通过该命令工具实现SQL语句、脚本的执行&#xff0c;并且可以实现脚本任务的自动化。 2、使用场景 2.1 针对大文件脚本的执…

Sqlcmd使用详解

Sqlcmd实用工具&#xff0c;可以输入 TRANSACT-SQL 语句、 系统过程和脚本文件&#xff0c;通过各种可用模式&#xff1a; 通过命令提示符。在中查询编辑器在 SQLCMD 模式下。在 Windows 脚本文件。在 SQL Server 代理作业的操作系统 (Cmd.exe) 作业步骤。 该实用工具使用 OD…

如何在CMD下写SQL语句

1、WinX->搜索环境变量 2、选择环境变量 3、在系统变量中找到Path&#xff0c;并双击进入 4、选择新建 5、选择安装MySQL的盘&#xff0c;找到bin&#xff0c;并复制路径到刚刚新建的Path中&#xff0c;例如笔者安装的盘是D盘 6、把复制好的路径放到Path中 7、WinR->直…

jquery设置cookie、删除cookie、获取cookie

1.引入两个js 去bootcdn搜索就行。 jquery.js <script src"https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>jquery cookie <script src"https://cdn.bootcdn.net/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.js"…

关闭浏览器 清除cookie

根据客户要求使用dedecms v5.6版本&#xff0c;发现了一个会员登录的bug&#xff01;看来dedecms也不过如此&#xff01; 首先说说bug所在&#xff1a;在会员登录页面 /member/index.php 登录的时候 有效期如果选择‘即时’&#xff0c;那么就永远也登不上去了&#xff01; 原因…

删除cookie删不掉的问题

文章转自&#xff1a; https://blog.csdn.net/swimming_in_it_/article/details/80290547 https://blog.csdn.net/huanshiwushuang/article/details/70172536 http://php.js.cn/blog/delete-cookie/ 不知道能不能解决你的问题,但是我来说说我的经历&#xff0c;昨天在用Yii…

Javascript清除所有的cookie

只需一个js函数即可&#xff1a; //清除所有cookie函数function clearAllCookie() {var keys document.cookie.match(/[^ ;](?\)/g);if(keys) {for(var i keys.length; i--;)document.cookie keys[i] 0;expires new Date(0).toUTCString()}} 测试的所有代码如下&#xf…

JS创建、获取、删除 cookie 方法

js 创建 cookie 方法 // 没有设置 cookie 过期时间的话&#xff0c;默认是 会话cookie(浏览器关闭时&#xff0c;该cookie失效&#xff0c;被删除) document.cookie cname "" value; // 设置了过期时间的话&#xff0c;在设置的时间段内 cookie 一直存在有效&…