命令行参数中&的含义?《Linux编程接口》代码技术问询
在Unix/Linux的shell环境里,&是个用来控制作业运行方式的特殊符号,核心作用就是让你的命令在后台偷偷跑,不用占着当前终端的前台会话。具体给你拆解下:
- 后台执行,不堵终端:当你在命令末尾加
&,shell会启动这个进程,但不会傻等它跑完,立刻给你返回命令提示符,你接着敲其他命令完全不耽误。比如find / -name "*.log" &,搜日志的命令在后台跑,你该干嘛干嘛。 - 作业控制相关:后台进程还是和当前终端绑定的,要是你关了终端,它大概率会收到
SIGHUP信号直接挂掉。想管它的话,用jobs命令能看当前shell的所有后台作业,fg %1(1是作业号)能把后台进程拉回前台,bg %1能让暂停的后台进程继续跑。 - 输出要注意:默认情况下,后台进程的输出还是会蹦到当前终端上,可能会打断你正在敲的命令。嫌烦的话就重定向,比如
command &> /dev/null &,把输出全丢去黑洞。 - 别和
&&搞混:单个&是管后台运行的,&&是逻辑与,只有前面的命令跑成功了(退出码是0),才会执行后面的命令,完全不是一回事哈。
先把代码补全(原代码有个明显的printf笔误,我按书本上下文补了下):
#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include "get_num.h" // 书本里自定义的整数解析工具头文件 #include "error_functions.h" // 错误处理函数头文件 static void handler(int sig) { // 这里是信号处理逻辑,示例里省略了,通常会做比如记录信号、设置标志位这类操作 // 注意:信号处理函数里只能调用异步信号安全的函数,不然容易出问题 } int main(int argc, char *argv[]) { int n, numSecs; sigset_t pendingMask, blockingMask, emptyMask; printf("%s: PID is %ld\n", argv[0], (long) getpid()); // 给所有信号都注册同一个处理函数 for (n = 1; n < NSIG; n++) (void) signal(n, handler); // 用(void)忽略返回值,因为有些信号(比如SIGKILL)没法捕获,调用会失败 if (argc > 1) { // 解析命令行参数,要求必须是大于0的整数 numSecs = getInt(argv[1], GN_GT_0, NULL); // 把blockingMask初始化包含所有信号,也就是所有信号都会被阻塞 sigfillset(&blockingMask); // 设置进程的信号掩码为blockingMask,从此所有信号都会被挂起来,暂不处理 if (sigprocmask(SIG_SETMASK, &blockingMask, NULL) == -1) errExit("sigprocmask"); printf("%s: sleeping for %d seconds\n", argv[0], numSecs); sleep(numSecs); // 睡眠指定时间,这段时间里收到的信号都会被阻塞 // 书本里这段代码后面应该还有逻辑:比如用sigpending获取待处理的信号,然后解除阻塞让信号触发handler // 比如: // sigpending(&pendingMask); // sigemptyset(&emptyMask); // sigprocmask(SIG_SETMASK, &emptyMask, NULL); } exit(EXIT_SUCCESS); }
咱们来逐段拆解这段代码的意图:
信号处理函数
handler:
这是个静态函数,专门用来处理收到的信号。因为是示例,所以省略了具体逻辑,但实际用的时候,这里一般会做些轻量操作——比如设置一个全局变量标志位,或者用write(异步安全)打印信号信息,绝对不能随便用printf这种非异步安全的函数,不然容易触发竞态条件搞崩程序。给所有信号注册handler:
循环for (n = 1; n < NSIG; n++)遍历了系统里所有可用的信号(NSIG是系统定义的信号总数,一般60多),用signal把每个信号的处理函数都设成handler。这里用(void)忽略返回值,是因为有些信号(比如SIGKILL和SIGSTOP)是内核强制不能被捕获或忽略的,调用signal会失败,但示例重点不在这,就直接忽略了。命令行参数解析:
如果用户传了参数,就用书本里自定义的getInt函数解析出睡眠秒数,GN_GT_0是告诉函数必须要大于0的整数,不然直接报错退出。阻塞所有信号:
sigfillset(&blockingMask):把这个信号集填满所有信号,相当于“我要阻塞所有信号”的配置。sigprocmask(SIG_SETMASK, &blockingMask, NULL):调用这个系统调用把进程的信号掩码换成blockingMask,从此之后,进程收到的任何信号都会被暂时“扣下来”,放到pending队列里,直到我们解除阻塞为止。
睡眠和后续逻辑:
睡眠指定秒数的这段时间里,不管外界给进程发什么信号,都会被阻塞。等睡眠结束后,书本里的示例一般会接着做两件事:用sigpending获取当前pending的信号列表,然后用sigprocmask把信号掩码改成空集(解除所有阻塞),这时候之前被扣下来的信号就会立刻触发handler处理——这也是这段代码的核心:演示信号阻塞、pending队列,以及解除阻塞后的信号递送流程。
内容的提问来源于stack exchange,提问作者Sigma65535

