【问题标题】:Signal handling for background processes后台进程的信号处理
【发布时间】:2017-04-01 10:24:58
【问题描述】:

我正在尝试在 C 中实现一个简单的 Unix shell。但是,我无法通过使用 sigaction 来实现后台进程功能。我的代码结构如下:

int main() {
struct sigaction act;
act.sa_handler = handler;

sigaction(SIGCHLD, &act, 0);

while(1) {
    parseCommand();
    execCommand();
}
}

另外,在我的 execCommand 函数中,结构如下:

if (fork()) {
    if (!isBackground) {
        wait(&child_status);
    }
    else
    printf("Background process\n");
}
else
    ... // Command execution...

我的处理程序如下:

void handler(int s) {
while (waitpid(-1, NULL, WNOHANG) > 0) {
}
printf("OK.\n");
}

但是这种结构会损害我的整个实现。甚至前台进程也无法正常工作。当我执行前台命令时,它给出:

Segmentation fault (core dumped) 

当我执行后台命令时,它给出:

Bus error (core dumped)

那么,我该如何解决这个问题呢?

提前谢谢...

编辑:当我使用 valgrind 调试我的代码时,它会给出以下信息:

==6768== Memcheck, a memory error detector
==6768== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==6768== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==6768== Command: ./shell
==6768== 
==6768== Syscall param rt_sigaction(act->sa_mask) points to uninitialised byte(s)
==6768==    at 0x4E6F5AE: __libc_sigaction (sigaction.c:62)
==6768==    by 0x400E90: main (in /home/enes/Desktop/Shell/shell)
==6768==  Address 0xffefff908 is on thread 1's stack
==6768== 
==6768== Syscall param rt_sigaction(act->sa_flags) points to uninitialised byte(s)
==6768==    at 0x4E6F5AE: __libc_sigaction (sigaction.c:62)
==6768==    by 0x400E90: main (in /home/enes/Desktop/Shell/shell)
==6768==  Address 0xffefff8f8 is on thread 1's stack
==6768== 
> ls
==6768== Use of uninitialised value of size 8
==6768==    at 0x4EA8C80: _IO_getline_info (iogetline.c:77)
==6768==    by 0x4EA7B7C: fgets (iofgets.c:53)
==6768==    by 0x400EC9: main (in /home/enes/Desktop/Shell/shell)
==6768== 
==6768== Invalid write of size 1
==6768==    at 0x4EA8C80: _IO_getline_info (iogetline.c:77)
==6768==    by 0x4EA7B7C: fgets (iofgets.c:53)
==6768==    by 0x400EC9: main (in /home/enes/Desktop/Shell/shell)
==6768==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==6768== 
==6768== 
==6768== Process terminating with default action of signal 11 (SIGSEGV)
==6768==  Access not within mapped region at address 0x0
==6768==    at 0x4EA8C80: _IO_getline_info (iogetline.c:77)
==6768==    by 0x4EA7B7C: fgets (iofgets.c:53)
==6768==    by 0x400EC9: main (in /home/enes/Desktop/Shell/shell)
==6768==  If you believe this happened as a result of a stack
==6768==  overflow in your program's main thread (unlikely but
==6768==  possible), you can try to increase the size of the
==6768==  main thread stack using the --main-stacksize= flag.
==6768==  The main thread stack size used in this run was 8388608.
==6768== 
==6768== HEAP SUMMARY:
==6768==     in use at exit: 0 bytes in 0 blocks
==6768==   total heap usage: 2 allocs, 2 frees, 2,048 bytes allocated
==6768== 
==6768== All heap blocks were freed -- no leaks are possible
==6768== 
==6768== For counts of detected and suppressed errors, rerun with: -v
==6768== Use --track-origins=yes to see where uninitialised values come from
==6768== ERROR SUMMARY: 4 errors from 4 contexts (suppressed: 0 from 0)
Segmentation fault (core dumped)

【问题讨论】:

  • 您首先要在调试器中捕捉前台处理的崩溃。当您发现崩溃时,请在代码中找到它发生的位置,以及值或所有涉及的变量是什么,并检查它们是否有效。如果您自己无法解决,请编辑问题以包含这些详细信息。
  • 至于子进程,你做的基本相同,但可能需要将调试器附加到子进程。
  • Valgrind 输出显示在您的 main 函数中某处(使用调试信息构建,构建时添加 -g 标志以获得确切位置)您使用无效参数调用 fgets。跨度>
  • 我的 fget 是这样的fgets(input, 513, stdin);,其中输入是一个 char*,513 是命令中的最大字符数。
  • 然后你初始化 input 变量?你真的把它指向某个地方吗?因为 Valgrind 说你没有。为什么你没有一个 array 而不是一个指针?

标签: c background-process sigchld sigaction


【解决方案1】:
Syscall param rt_sigaction(act->sa_mask) points to uninitialised byte(s)

您需要完全初始化sigaction 结构,否则它包含不确定的值。读取这些(可能通过sigaction() 函数调用)很可能会导致Undefined Behaviour,任何事情都可能发生。

修复此更改

struct sigaction act;

成为

struct sigaction act = {0};

可能不是您面临的问题,但仍然:

不能保存从信号处理程序调用printf()-family 函数的任何成员。

更重要的是:可以从信号处理程序安全调用的函数集是(非常)有限的。如需完整列表,请参阅chapter 2.4.3 on this page

修复此更改

printf("OK.\n");

成为

write(STDERR_FILENO, "OK.\n", sizeof "OK.\n" -1);

或类似的。根据上面链接的列表,write() 保存为从信号处理程序调用。


==6768== Use of uninitialised value of size 8
==6768==    at 0x4EA8C80: _IO_getline_info (iogetline.c:77)
==6768==    by 0x4EA7B7C: fgets (iofgets.c:53)

fgets() 的调用实际上有什么问题我们无法判断,因为您没有向我们展示相关代码。

【讨论】:

  • 其实我的目的是测试handler是否在工作。
  • fileno() 也不是异步信号安全的。可以改用STDERR_FILENO(或2)。
  • @P.P.: 哦,知道了! :} 谢谢,已修复。
  • 嗯,memset 是“初始化”sigaction 的非便携式方式。对于act.sa_mask,您必须使用 sigemptyset() 或 sigfillset() 之一。
猜你喜欢
  • 1970-01-01
  • 2019-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多