我写了一个简单的外壳。
为了处理僵尸进程,我使用signal(SIGHLD, signal_handler)
和自定义的hadler。
当我以某种方式运行该程序时,gdb会告诉我孩子收到了SIGTTIN
并且程序停止了。
但是,如果我将处理程序更改为while(waitpid(-1, &status, WNOHANG)>0);
,一切都会顺利进行。
我想知道为什么此更改会导致错误。经过一些谷歌,它似乎与“子进程”信号处理程序有关。 但是,此处理程序永远不会尝试从stdin或类似的东西读取。
有人可以告诉我为什么会发生这种情况以及它如何工作吗? 或给我一些关键词进行搜索。 谢谢您的帮助!
#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
答案 0 :(得分:0)
我无法使用您提供的代码重现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
而消失的其他错误时,循环终止再次)。 man page for waitpid
说:
返回值
成功时,返回状态已更改的子进程的ID。如果指定了WNOHANG,并且存在由pid指定的一个或多个孩子,但尚未更改状态,则返回0。出错时,返回-1。
错误
ECHILD由pid指定的进程不存在或不是调用进程的子进程。
未设置EINTR WNOHANG,并且捕获到畅通的信号或SIGCHLD。
EINVAL options参数无效。
所以我将代码更改为:
void sig_handle(int sig){
int status;
while (waitpid(-1, &status, WNOHANG) == 0); // more precisely, break if r > 0 || r == -1
}
1 :信号处理程序中的printf是Bad Thing(TM),但通常可以调试。