我写了一个简单的shell。
为了处理僵尸进程,我使用了 signal(SIGHLD, signal_handler)
和一个自定义的 hadler。
当我以某种方式运行这个程序时,gdb 会告诉我 child 收到了 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。
我的环境是 Ubuntu 18.04,带有 bash 和 g++
最佳答案
我无法使用您提供的代码重现您的 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< 不会消失的一些其他错误)/code> 再次)。 man page for waitpid 说:
RETURN VALUE
on success, returns the process ID of the child whose state has changed; if WNOHANG was specified and one or more child(ren) specified by pid exist, but have not yet changed state, then 0 is returned. On error, -1 is returned.
ERRORS
ECHILD The process specified by pid does not exist or is not a child of the calling process.
EINTR WNOHANG was not set and an unblocked signal or a SIGCHLD was caught.
EINVAL The options argument was invalid.
所以我将代码更改为:
void sig_handle(int sig){
int status;
while (waitpid(-1, &status, WNOHANG) == 0); // more precisely, break if r > 0 || r == -1
}
1:信号处理程序中的 printf 是一件坏事(TM),但通常可以用于调试。
关于c++ - 为什么我的自定义信号处理程序会让子进程在比较 pid 时得到 SIGTTIN,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52796755/