我有两个节目:1)父亲2) 。当父亲收到SIGINT (CTRL)信号时,他的处理程序向他的孩子发送一个SIGTERM。问题是,通常(不总是,不知道为什么)它会在SIGINT之后的循环中显示这个错误:
Invalid Argument父亲的目标是创造一个孩子,然后活着,准备好应付SIGINT。
父亲
#include "library.h"
static void handler();
int main(int argc, char* argv[]){
int value, que_id;
char str_que_id[10], **child_arg;
pid_t child_pid;
sigaction int_sa;
//Create message queue
do{
que_id = msgget(IPC_PRIVATE, ALL_PERM | IPC_CREAT);
}while(que_id == -1);
snprintf(str_que_id, sizeof(str_que_id), "%d", que_id);
//Set arguments for child
child_arg = malloc(sizeof(char*) * 3);
child[0] = "child";
child[1] = str_que_id;
child[2] = NULL;
//Set handler for SIGINT
int_sa.sa_handler = &handler;
int_sa.sa_flags = SA_RESTART;
sigemptyset(&int_sa.sa_mask);
sigaddset(&int_sa.sa_mask, SIGALRM);
sigaction(SIGINT, &int_sa, NULL);
//Fork new child
if(value = fork() == 0){
child_pid = getpid();
do{
errno = 0;
execve("./child", child_arg, NULL);
}while(errno);
}
//Keep alive father
while(1);
return 0;
}
static void handler(){
if(kill(child_pid, SIGTERM) != -1)
waitpid(child_pid, NULL, WNOHANG);
while(msgctl(que_id, IPC_RMID, NULL) == -1);
free(child_arg);
exit(getpid());
}子项目(仅在我的项目中)的目标是等待从消息队列传入的新消息。因为不会有任何消息,它将永远被封锁。
孩子
#include "library.h"
typedef struct _Msgbuf {
long mtype;
char[10] message;
} Msgbuf;
int main(int argc, char * argv[]){
int que_id;
//Recovery of message queue id
que_id = atoi(argv[1]);
//Set handler for SIGTERM
signal(SIGTERM, handler);
//Dynamic allocation of message
received = calloc(1, sizeof(Msgbuf));
while(1){
do{
errno = 0;
//This will block child because there won't be any message incoming
msgrcv(que_id, received, sizeof(Msgbuf) - sizeof(long), getpid(), 0);
if(errno)
perror(NULL);
}while(errno && errno != EINTR);
}
}
static void handler(){
free(received);
exit(getpid());
}我从msgrcv()那里知道
调用进程捕获一个信号。在这种情况下,如果errno设置为EINTR,系统调用就会失败。(msgrcv()在被信号处理程序中断后永远不会自动重新启动,无论在建立信号处理程序时SA_RESTART标志的设置如何。)
那么,为什么要循环打印这个错误呢?它应该退出处理程序,相反,在处理程序返回之后,它似乎找不到消息设置的缓冲区errno到EINVAL。
发布于 2018-08-14 18:47:45
你的程序有很多问题。它通过从信号处理程序中调用exit、free和msgctl来调用未定义的行为。Open规范的信号作用部分中的表列出了在信号处理程序中安全调用的函数。在大多数情况下,您只想从处理程序中切换一个“正在运行”标志,并让主循环运行,直到它被告知退出为止。类似于下面的简单示例:
#include <signal.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
/* this will be set when the signal is received */
static sig_atomic_t running = 1;
void
sig_handler(int signo, siginfo_t *si, void *context)
{
running = 0;
}
int
main(int argc, char *argv[])
{
int rc;
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = &sig_handler;
rc = sigaction(SIGINT, &sa, NULL);
if (rc < 0) {
perror("sigaction");
exit(EXIT_FAILURE);
}
printf("Waiting for SIGINT\n");
while (running) {
printf("... sleeping for 10 seconds\n");
sleep(10);
}
printf("Signal received\n");
return 0;
}我还在repl.it上设置了一个更复杂的会话。
另一个问题是假设errno在函数调用中保留一个零值。这很可能是这样的,但是关于errno,您应该假设的唯一一件事是,当库函数返回一个失败代码时,它将被分配一个值--例如,read返回-1并将errno设置为指示错误的东西。调用C运行时库函数的传统方法是检查返回值,并在适当情况下咨询errno:
int bytes_read;
unsigned char buf[128];
bytes_read = read(some_fd, &buf[0], sizeof(buf));
if (bytes_read < 0) {
printf("read failed: %s (%d)\n", strerror(errno), errno);
}您的应用程序可能是循环运行的,因为父程序行为不当,没有等待子程序或类似的东西(请参阅上面关于未定义行为的说明)。如果在子队列退出之前删除了消息队列,那么msgrcv调用将失败,并将errno设置为EINVAL。在检查msgrcv之前,应该先检查errno是否失败。当循环遇到与msgrcv相同的errno失败时,子循环也应该终止,因为这是一个终端条件--匿名消息队列在它停止存在后永远无法重新创建。
发布于 2018-08-11 15:31:40
(几乎)只有在函数调用失败的情况下,errno才会带一个正常的值。
这就是msgrcv()的情况。
来自[经]文件
返回值 成功完成后,
msgrcv()将返回一个等于实际放置在缓冲区mtext中的字节数的值。否则,将不会收到任何消息,msgrcv()将返回-1,而errno将被设置为指示错误。
因此,只有当errno返回-1时才使用errno,否则errno的值是未定义的,它很可能包含垃圾或不包含垃圾.
下面的代码没有意义..。
msgrcv(que_id, received, sizeof(Msgbuf) - sizeof(long), getpid(), 0);
if(errno)
perror(NULL);
} while(errno && errno != EINTR);..。看起来应该是:
if (-1 == msgrcv(que_id, received, sizeof(Msgbuf) - sizeof(long), getpid(), 0))
{
/* Only here errno had a well defined value. */
perror("msgrcv() failed"); /* perror() translates errno into a human readable text prefixed by its argument and logs it to the stderr. */
}
else
{
errno = 0;
}
} while (errno && errno != EINTR);这个BTW
do{
errno = 0;
execve("./child", child_arg, NULL);
}while(errno);只作为exec*()系列函数的成员工作,只返回错误。因此,当测试while的条件时,execve()失败了,尽管已经设置了errno,这里的初始errnr = 0;设置也是无用的。
https://stackoverflow.com/questions/51799781
复制相似问题