HP-UX上的GCC、大量轮询()、管道()和文件问题
我在构建一个“中间人”记录器时遇到了很多麻烦-目的是将其放置在/usr/bin中项目上方的路径上,并捕获进出应用程序的所有内容。(黑盒第三方应用程序由于某些原因无法通过FTP。)一旦运行,中间人将分叉,将stdout和stdin重定向到父级控制的管道,然后在/usr/bin中执行程序。(硬编码;是的,我知道,我很糟糕。) 然而,一旦我运行poll(),事情就会变得很奇怪。我丢失了日志文件的句柄,孩子在输出管道上的投票抛出了一个错误,猫和狗开始住在一起,等等 有人能解释一下吗 这是我目前拥有的。。。所讨论的poll()标记为非缩进注释,以便于定位HP-UX上的GCC、大量轮询()、管道()和文件问题,c,pipe,polling,C,Pipe,Polling,我在构建一个“中间人”记录器时遇到了很多麻烦-目的是将其放置在/usr/bin中项目上方的路径上,并捕获进出应用程序的所有内容。(黑盒第三方应用程序由于某些原因无法通过FTP。)一旦运行,中间人将分叉,将stdout和stdin重定向到父级控制的管道,然后在/usr/bin中执行程序。(硬编码;是的,我知道,我很糟糕。) 然而,一旦我运行poll(),事情就会变得很奇怪。我丢失了日志文件的句柄,孩子在输出管道上的投票抛出了一个错误,猫和狗开始住在一起,等等 有人能解释一下吗 这是我目前拥有的。。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <time.h>
#include <sys/types.h>
#include <fcntl.h>
#define MAX_STR_LEN 1024
static int directionFlag; /* 0 = input, 1 = output */
static int eofFlag;
/* Splits the next char from the stream inFile, with extra
information logged if directionFlag swaps */
void logChar(int inFilDes, int outFilDes, FILE *logFile, int direction)
{
char inChar = 0;
if(read(inFilDes, &inChar, sizeof(char)) > 0)
{
if(direction != directionFlag)
{
directionFlag = direction;
if(direction)
{
fprintf(logFile, "\nOUTPUT: ");
} else {
fprintf(logFile, "\nINPUT: ");
}
}
write(outFilDes, &inChar, sizeof(char));
fputc(inChar, stderr);
fputc(inChar, logFile);
} else {
eofFlag = 1;
}
return;
}
int main(int argc, char* argv[])
{
pid_t pid;
int childInPipe[2];
int childOutPipe[2];
eofFlag = 0;
/* [0] is input, [1] is output*/
if(pipe(childInPipe) < 0 || pipe(childOutPipe) < 0) {
fprintf(stderr,"Pipe error; aborting\n");
exit(1);
}
if((pid = fork()) == -1){
fprintf(stderr,"Fork error; aborting\n");
exit(1);
}
if(pid)
{
/*Parent process*/
int i;
int errcode;
time_t rawtime;
struct tm * timeinfo;
time(&rawtime);
timeinfo=localtime(&rawtime);
struct pollfd pollArray[2] = {
{ .fd = 0, .events = POLLIN, .revents = 0 },
{ .fd = childOutPipe[0], .events = POLLIN, .revents = 0 }
};
/* Yet again, 0 = input, 1 = output */
nfds_t nfds = sizeof(struct pollfd[2]);
close(childInPipe[0]);
close(childOutPipe[1]);
/* We don't want to change around the streams for this one,
as we will be logging everything - and I do mean everything */
FILE *logFile;
if(!(logFile = fopen("/opt/middleman/logfile.txt", "a"))) {
fprintf(stderr, "fopen fail on /opt/middleman/logfile.txt\n");
exit(1);
}
fprintf(logFile, "Commandline: ");
for(i=0; i < argc; i++)
{
fprintf(logFile, "%s ", argv[i]);
}
fprintf(logFile, "\nTIMESTAMP: %s\n", asctime(timeinfo));
while(!eofFlag)
{
// RIGHT HERE is where things go to pot
errcode = poll(pollArray, nfds, 1);
// All following fprintf(logfile)s do nothing
if(errcode < 0) {
fprintf(stderr, "POLL returned with error %d!", errcode);
eofFlag = 1;
}
if((pollArray[0].revents && POLLERR) & errno != EAGAIN ) {
fprintf(stderr, "POLL on input has thrown an exception!\n");
fprintf(stderr, "ERRNO value: %d\n", errno);
fprintf(logFile, "POLL on input has thrown an exception!\n");
eofFlag = 1;
} else if(pollArray[0].revents && POLLIN) {
logChar(pollArray[0].fd, childInPipe[1], logFile, 0);
} else if((pollArray[1].revents && POLLERR) & errno != EAGAIN ) {
fprintf(stderr, "POLL on output has thrown an exception!\n");
fprintf(stderr, "ERRNO value: %d\n", errno);
fprintf(logFile, "POLL on output has thrown an exception!\n");
eofFlag = 1;
} else if(pollArray[1].revents && POLLIN) {
logChar(pollArray[1].fd, 1, logFile, 1);
}
}
fclose(logFile);
}
else
{
/*Child process; switch streams and execute application*/
int i;
int catcherr = 0;
char stmt[MAX_STR_LEN] = "/usr/bin/";
close(childInPipe[1]);
close(childOutPipe[0]);
strcat(stmt, argv[0]);
if(dup2(childInPipe[0],0) < 0) {
fprintf(stderr, "dup2 threw error %d on childInPipe[0] to stdin!\n", errno);
}
// close(childInPipe[0]);
if(dup2(childOutPipe[1],1) < 0)
{
fprintf(stderr, "dup2 threw error %d on childInPipe[1] to stdout!\n", errno);
}
/* Arguments need to be in a different format for execv */
char* args[argc+1];
for(i = 0; i < argc; i++)
{
args[i] = argv[i];
}
args[i] = (char *)0;
fprintf(stderr, "Child setup complete, executing %s\n", stmt);
fprintf(stdout, "Child setup complete, executing %s\n", stmt);
if(execv(stmt, args) == -1) {
fprintf(stderr, "execvP error!\n");
exit(1);
}
}
return 0;
}
日志文件具有以下内容:
Commandline: banner testing
TIMESTAMP: Tue Jun 23 11:21:00 2009
ERRNO中包含0的原因是poll()返回的结果很好;是pollArray[1]。revents返回时出错,这意味着childOutPipe[0]被轮询为有错误。据我所知,logChar()从未被调用
我将尝试将poll()拆分为两个不同的调用。
好的,当我poll()时-即使在stdin上,它也不会返回错误消息-它会破坏我写入日志文件的能力。此外,我还发现while()循环运行了好几次,然后输出轮询返回并在管道上出错。我越来越相信poll()只是一个失败的事业。
每次尝试写入日志文件都会在poll()之后失败,即使是成功的poll(),errno设置为“Bad file number”。这真的不应该发生。我真的看不出它会如何影响我的文件处理。
好吧,显然我是个白痴。谢谢你帮我澄清;我假设NFD是字节大小,而不是数组大小。这是固定的,瞧!它不会再杀死我的日志文件句柄。真正的问题: 第一个(但较小)问题 您可能对“struct pollfd”的顺序和内容做出了不必要的假设。标准所说的是它包含(至少)三个成员;它没有说明它们出现的顺序 标题应定义pollfd结构,该结构应至少包括以下构件:
int fd The following descriptor being polled.
short events The input event flags (see below).
short revents The output event flags (see below).
由于您使用的是C99,请使用安全初始化符号:
struct pollfd pollArray[2] =
{
{ .fd = 0, .events = POLLIN, .revents = 0 },
{ .fd = childOutPipe[0], .events = POLLIN, .revents = 0 },
};
您可以将标准输入的0替换为
中的FILENO\u STDIN
第二(主要)问题
轮询数组的大小可能是16(字节)-在大多数但不是所有机器上(32位和64位)。您需要轮询数组的维度(2)。这就是为什么所有的地狱都会松动;系统正在查看垃圾,并变得混乱
发表评论:
要查找在本地文件或函数中定义的数组的维度(但不是传递到函数中的数组参数,也不是在另一个文件中定义的数组),请使用宏的变体:
#define DIM(x) (sizeof(x)/sizeof(*(x)))
这个名字可以追溯到遥远的过去,在昏暗的环境中使用BASIC;我见过的其他名称是NELEMS
或ARRAY\u SIZE
或DIMENSION
(回到Fortran IV),我相信还有很多其他名称
所发生的情况是,因为您没有将nfds
设置为2,所以系统调用正在读取实际struct pollfd
数组之后的数据,并尝试生成不是struct pollfd
的内容的开头或结尾。特别是,它可能正在写入您告诉它的struct pollfd
数组中一行的revents
字段,但实际空间是日志文件*
,因此这完全是错误的。对于其他局部变量也是如此。换句话说,您有一个堆栈缓冲区溢出-也称为堆栈溢出,这个名称应该有点熟悉。但它之所以发生是因为你对它进行了编程
修正:
第三(中级)问题
请注意添加到错误消息中的换行符-您需要它。或:
if (poll(pollArray, nfds, 1) < 0)
{
int errnum = errno;
fprintf(stderr, "POLL returned with error (%d: %s)\n",
errnum, strerror(errnum));
eofFlag = 1;
}
if(轮询(轮询数组,nfds,1)<0)
{
int errnum=errno;
fprintf(stderr,“返回的轮询有错误(%d:%s)\n”,
errnum,strerror(errnum));
eofFlag=1;
}
在第二种情况下,您需要将“#include
”添加到标题列表中。保存errno
的值可以防止函数调用对其进行更改,但只有在函数(系统调用)失败时才能可靠地测试errno
。即使成功的函数调用也可能使errno
非零。(例如,在某些系统上,如果stderr
不发送到终端,则在I/O调用后errno
的值为ENOTTY
,即使整个调用成功。)
先前的沉思 关于可能出现的问题的一些事先想法;我想下面还有一些有用的信息 我怀疑您的问题是
poll()
会“损坏”轮询描述符集,您必须在每个循环中重新构建它。(在检查了手册页后,似乎poll()
没有select()
遇到的问题。)这肯定是相关的select()
系统调用的问题
您的子代码没有在应该关闭的时候关闭所有文件描述符-您已经注释掉了一个“close()”,而另一个完全丢失了。当子级完成将管道连接到标准输入和输出后,您不希望未复制的文件描述符仍然打开;进程无法正确检测EOF
类似的意见可能适用于母公司
另外,请注意,发送过程可能需要在孩子的标准输出上出现任何内容之前向孩子发送多个数据包。作为一个极端情况,考虑“<代码>排序< /代码>”;在生成任何输出之前读取其所有数据的。我担心方向转换码,因此,
nfds_t nfds = sizeof(pollArray);
#define DIM(x) (sizeof(x)/sizeof(*(x)))
nfds_t nfds = DIM(pollArray);
poll(pollArray, nfds, 1);
if (errcode < 0) {
errcode = poll(pollArray, nfds, 1);
if (errcode < 0)
{
fprintf(stderr, "POLL returned with error %d!\n", errcode);
eofFlag = 1;
}
if (poll(pollArray, nfds, 1) < 0)
{
int errnum = errno;
fprintf(stderr, "POLL returned with error (%d: %s)\n",
errnum, strerror(errnum));
eofFlag = 1;
}