c++ - 为什么我的自定义信号处理程序会使子进程在比较 pid 时获得 SIGTTIN
问题描述
我写了一个简单的shell。为了处理僵尸进程,我使用signal(SIGHLD, signal_handler)
了一个自定义的hadler。
当我以某种方式运行这个程序时,gdb 会告诉我收到孩子SIGTTIN
并且程序停止了。但是,如果我将处理程序更改为while(waitpid(-1, &status, WNOHANG)>0);
,一切都会顺利进行。
我想知道为什么这种更改会导致错误。经过一番谷歌,它似乎与“子进程”信号处理程序有关。但是,这个处理程序从不尝试从标准输入或类似的东西读取。
谁能告诉我为什么会发生这种情况以及它是如何工作的?或者给我一些关键词搜索。感谢您的帮助!
#include<iostream>
#include<sstream>
#include<string>
#include<vector>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
using namespace std;
int parse_argv(stringstream &in_command, char** &argv); //get argv list
void sig_handle(int sig);
int main()
{
while(true){
string input, output;
stringstream ss;
char **myArgv;
int myArgc;
bool back_ground = false;
cout << ">";
getline(cin, input);
ss << input;
myArgc = parse_argv(ss, myArgv); //get argument!
if(strcmp(myArgv[myArgc-1], "&") == 0){ //check if & exit
back_ground = true;
myArgv[myArgc-1] = NULL;
}
pid_t pid = fork();
if(pid < 0){
cout << "Fork process error!" << endl;
}
else if(pid == 0){ //child process
execvp(myArgv[0], myArgv);
exit(0);
}
else{ //parent process
if(back_ground == false)
wait(&pid);
else
signal(SIGCHLD, sig_handle);
//Free memory!!
for(int i=0; i<=myArgc; i++) //size of myArgv is myArgc+1
delete[] myArgv[i];
delete[] myArgv;
}
}
return 0;
}
int parse_argv(stringstream &in_command, char** &argv){
vector<string> tmp;
string tmp_s;
tmp.reserve(10);
while(in_command >> tmp_s){
tmp.push_back(tmp_s);
}
argv = new char* [tmp.size()+1]; //dynamic allocate true arugments array
for(int i=0; i<tmp.size(); i++){
argv[i] = new char[strlen(tmp[i].c_str())];
strcpy(argv[i], tmp[i].c_str());
}
argv[tmp.size()] = NULL; //argv should terminated by NULL
return tmp.size();
}
void sig_handle(int sig){
int status;
while(waitpid(-1, &status, WNOHANG));
}
如何重复错误:
- 编译并运行这个程序
- 类型
ls &[Enter]
- 类型
ls[Enter]
- 然后你会发现第二个
ls
没有返回任何东西。在 gdb 中,您将在第 38 行看到子进程获取 SIGTTIN。
我的环境是带有 bash 和 g++ 的 Ubuntu 18.04
解决方案
我无法使用您提供的代码重现您的 SIGTTIN 错误,这可能只是由于您使用的 gdb 命令的确切顺序,但是在我给它以下输入后,您的程序确实变得无响应:
ls &
ls
gdb 显示该while
语句处于无限循环中:
void sig_handle(int sig){
int status;
while(waitpid(-1, &status, WNOHANG));
}
添加 printf 以仔细查看1:
void sig_handle(int sig){
int status, i, r;
for (i=0; i<5; i++) {
errno = 0;
r = waitpid(-1, &status, WNOHANG);
printf("waitpid returns %d errno %d\n", r, errno);
sleep(1);
}
}
输出是:
waitpid returns 3672 errno 0
waitpid returns -1 errno 10
waitpid returns -1 errno 10
waitpid returns -1 errno 10
waitpid returns -1 errno 10
所以应该改变逻辑,以便在等待进程或没有进程等待(或其他一些不会通过waitpid
再次调用而消失的错误)时终止循环。waitpid的手册页
说:
返回值
成功时,返回状态已更改的子进程 ID;如果指定了 WNOHANG 并且存在一个或多个由 pid 指定的子(ren),但尚未更改状态,则返回 0。出错时,返回 -1。
错误
ECHILD pid 指定的进程不存在或不是调用进程的子进程。
未设置 EINTR WNOHANG 并且捕获了未阻塞的信号或 SIGCHLD。
EINVAL 选项参数无效。
所以我将代码更改为:
void sig_handle(int sig){
int status;
while (waitpid(-1, &status, WNOHANG) == 0); // more precisely, break if r > 0 || r == -1
}
1:信号处理程序中的 printf 是一个坏事(TM),但它通常可以用于调试。
推荐阅读
- javascript - 从子级中排除嵌套组件(Nuxt 路由)
- java - 即使在spring boot中安装了maven,mvn命令也不起作用
- r - 通过 ggplot + coord_trans 进行 log2 变换的逆
- hbase - 如何在不退出 hbase shell 的情况下修复以星号结尾的提示?
- powerbi - 为什么 FILTER 中的 VALUES 会导致恢复过滤器?
- regex - Notepad++ - 如果条件为真,如何替换 1 个字符
- javascript - JavaScript - 大数的除法和乘法
- python - 元组的关键字是什么?
- apache-spark - Kafka 服务器在我的本地计算机上,我的 spark 服务器在 aws 上,我收到错误“找不到数据源:kafka”。
- excel - .Firstpage.Rightheader 带图片的命令不起作用