epoll 的分段错误
Segmentation fault with epoll
我正在尝试制作一个执行以下操作的程序:
作为命令行参数,它接收 FIFO 文件的路径。它应该使用 epoll
API 来监控这些 FIFO。通过 FIFO,可以保证只发送浮点数。程序的输出应该是通过每个 FIFO 发送的数字的总和(当写入端关闭所有文件时程序停止)。
在我开始实际代码之前,这里有一个宏和一个函数,您将在整个程序中看到它:
#define iAssert(cond, msg) crash(cond, msg, __LINE__)
void crash(bool cond, char * msg, int line){
if(!cond){
perror(msg);
fprintf(stderr, "at line %d\n", line);
exit(EXIT_FAILURE);
}
}
只是一个简单的断言机制,与问题本身无关。
无论如何,首先我获取通过命令行参数传递的 FIFO 数量并创建一个 epoll
文件描述符:
int numFifos = argc - 1;
int epollFileDesc = epoll_create(1);
iAssert(-1 != epollFileDesc, "epoll_create");
然后我创建一个 fifo 文件描述符数组和一个总和数组,在接下来的循环中我将其初始化为零。
int * fifoFileDescriptors = malloc(numFifos * sizeof(int));
iAssert(NULL != fifoFileDescriptors, "malloc1");
float * localSums = malloc(numFifos * sizeof(float));
iAssert(NULL != localSums, "malloc 2");
到目前为止一切顺利,我想。以下循环除了初始化总和数组外,还打开 FIFO、填充先前的文件描述符数组并注册事件。
for(int i = 0; i<numFifos; i++){
localSums[i] = 0.f;
int thisFd = open(argv[i+1], O_RDONLY | O_NONBLOCK);
iAssert(-1 != thisFd, "open");
fifoFileDescriptors[i] = thisFd;
FILE * thisFs = fdopen(thisFd, "r");
iAssert(NULL != thisFs, "fdopen");
DataPass registerThis;
registerThis.fifoIndex = i;
registerThis.file = thisFs;
struct epoll_event thisEvent;
thisEvent.events = 0;
thisEvent.events |= EPOLLIN;
thisEvent.data.ptr = (void *)®isterThis;
iAssert(-1 != epoll_ctl(epollFileDesc, EPOLL_CTL_ADD, thisFd, &thisEvent), "epoll_ctl");
}
DataPass 结构如下所示:
typedef struct{
int fifoIndex;
FILE * file;
}DataPass;
如您所见,我想要的是接收文件流而不是文件描述符,因为它们更易于读取。除此之外,我保留了 FIFO 的索引,所以我知道它是哪一个。
在此之后我监控变化:
int nOpen = numFifos;
struct epoll_event events[MAX_EVENTS];
while(nOpen){
int active = epoll_wait(epollFileDesc, events, MAX_EVENTS, -1);
iAssert(-1 != active, "epoll_wait");
for(int i = 0; i<active; i++){
struct epoll_event thisEvent = events[i];
if(thisEvent.events & EPOLLIN){
DataPass * thisData = (DataPass *)thisEvent.data.ptr;
//fifo with index thisData->fifoIndex has sent a message
float x;
while(1 == fscanf(thisData->file, "%f", &x)){
localSums[thisData->fifoIndex] += x;
}
}else if (thisEvent.events & (EPOLLERR | EPOLLHUP)){
//need to close this connection
DataPass * thisData = (DataPass *)thisEvent.data.ptr;
iAssert(-1 != epoll_ctl(epollFileDesc, EPOLL_CTL_DEL, fifoFileDescriptors[thisData->fifoIndex], NULL), "epoll_ctl del");
fclose(thisData->file);
close(fifoFileDescriptors[thisData->fifoIndex]);
nOpen--;
}
}
}
MAX_EVENTS
宏定义为4.
在 运行 这之后(并使用辅助程序制作 fifos 并通过它们发送值)我得到了一个分段错误,我已经设法追踪到 fscanf
部分。尽管我已经找到它,但我仍然不知道它为什么会导致它。
有什么想法吗?
谢谢。
您正在通过保存指向局部变量的指针来调用未定义的行为,该指针超过了它所在的堆栈的有效性
for(int i = 0; i<numFifos; i++){
DataPass registerThis;
registerThis.file = thisFs;
thisEvent.data.ptr = (void *)®isterThis;
}
不要导出指向局部变量的指针,并在它们不再存在时尝试使用它们。以更持久的方式分配您的存储空间。
这一行:
DataPass registerThis;
在堆栈上声明一个结构。一旦再次循环,该内存将被新结构覆盖。然后你得到一个指向它的指针:
thisEvent.data.ptr = (void *)®isterThis;
循环结束后,这个指针没有指向任何有意义的东西。
要解决此问题,您需要在堆上分配该结构。
我正在尝试制作一个执行以下操作的程序:
作为命令行参数,它接收 FIFO 文件的路径。它应该使用 epoll
API 来监控这些 FIFO。通过 FIFO,可以保证只发送浮点数。程序的输出应该是通过每个 FIFO 发送的数字的总和(当写入端关闭所有文件时程序停止)。
在我开始实际代码之前,这里有一个宏和一个函数,您将在整个程序中看到它:
#define iAssert(cond, msg) crash(cond, msg, __LINE__)
void crash(bool cond, char * msg, int line){
if(!cond){
perror(msg);
fprintf(stderr, "at line %d\n", line);
exit(EXIT_FAILURE);
}
}
只是一个简单的断言机制,与问题本身无关。
无论如何,首先我获取通过命令行参数传递的 FIFO 数量并创建一个 epoll
文件描述符:
int numFifos = argc - 1;
int epollFileDesc = epoll_create(1);
iAssert(-1 != epollFileDesc, "epoll_create");
然后我创建一个 fifo 文件描述符数组和一个总和数组,在接下来的循环中我将其初始化为零。
int * fifoFileDescriptors = malloc(numFifos * sizeof(int));
iAssert(NULL != fifoFileDescriptors, "malloc1");
float * localSums = malloc(numFifos * sizeof(float));
iAssert(NULL != localSums, "malloc 2");
到目前为止一切顺利,我想。以下循环除了初始化总和数组外,还打开 FIFO、填充先前的文件描述符数组并注册事件。
for(int i = 0; i<numFifos; i++){
localSums[i] = 0.f;
int thisFd = open(argv[i+1], O_RDONLY | O_NONBLOCK);
iAssert(-1 != thisFd, "open");
fifoFileDescriptors[i] = thisFd;
FILE * thisFs = fdopen(thisFd, "r");
iAssert(NULL != thisFs, "fdopen");
DataPass registerThis;
registerThis.fifoIndex = i;
registerThis.file = thisFs;
struct epoll_event thisEvent;
thisEvent.events = 0;
thisEvent.events |= EPOLLIN;
thisEvent.data.ptr = (void *)®isterThis;
iAssert(-1 != epoll_ctl(epollFileDesc, EPOLL_CTL_ADD, thisFd, &thisEvent), "epoll_ctl");
}
DataPass 结构如下所示:
typedef struct{
int fifoIndex;
FILE * file;
}DataPass;
如您所见,我想要的是接收文件流而不是文件描述符,因为它们更易于读取。除此之外,我保留了 FIFO 的索引,所以我知道它是哪一个。
在此之后我监控变化:
int nOpen = numFifos;
struct epoll_event events[MAX_EVENTS];
while(nOpen){
int active = epoll_wait(epollFileDesc, events, MAX_EVENTS, -1);
iAssert(-1 != active, "epoll_wait");
for(int i = 0; i<active; i++){
struct epoll_event thisEvent = events[i];
if(thisEvent.events & EPOLLIN){
DataPass * thisData = (DataPass *)thisEvent.data.ptr;
//fifo with index thisData->fifoIndex has sent a message
float x;
while(1 == fscanf(thisData->file, "%f", &x)){
localSums[thisData->fifoIndex] += x;
}
}else if (thisEvent.events & (EPOLLERR | EPOLLHUP)){
//need to close this connection
DataPass * thisData = (DataPass *)thisEvent.data.ptr;
iAssert(-1 != epoll_ctl(epollFileDesc, EPOLL_CTL_DEL, fifoFileDescriptors[thisData->fifoIndex], NULL), "epoll_ctl del");
fclose(thisData->file);
close(fifoFileDescriptors[thisData->fifoIndex]);
nOpen--;
}
}
}
MAX_EVENTS
宏定义为4.
在 运行 这之后(并使用辅助程序制作 fifos 并通过它们发送值)我得到了一个分段错误,我已经设法追踪到 fscanf
部分。尽管我已经找到它,但我仍然不知道它为什么会导致它。
有什么想法吗?
谢谢。
您正在通过保存指向局部变量的指针来调用未定义的行为,该指针超过了它所在的堆栈的有效性
for(int i = 0; i<numFifos; i++){
DataPass registerThis;
registerThis.file = thisFs;
thisEvent.data.ptr = (void *)®isterThis;
}
不要导出指向局部变量的指针,并在它们不再存在时尝试使用它们。以更持久的方式分配您的存储空间。
这一行:
DataPass registerThis;
在堆栈上声明一个结构。一旦再次循环,该内存将被新结构覆盖。然后你得到一个指向它的指针:
thisEvent.data.ptr = (void *)®isterThis;
循环结束后,这个指针没有指向任何有意义的东西。
要解决此问题,您需要在堆上分配该结构。