【问题标题】:wait() returns -1, errno=10 after launching Python process启动 Python 进程后 wait() 返回 -1, errno=10
【发布时间】:2023-03-27 17:08:01
【问题描述】:

我有一个在线程上启动 Python 进程的 C 程序。 Python 进程使用 Selenium 并向 FTC 提交骚扰电话投诉。 posix_spawn 用于保持 C 程序和 Python 进程因 Python 中的内存泄漏而分离。我在管理 Python 进程时遇到了问题。

Python 进程的wait() 返回-1errno 设置为10。根据wait(2) man page,这是一个错误。我相信10ECHILDstrerror 返回 No child processes

$ ./test.exe
Process started, pid 2730
call_datetime: 2019-04-04 18:07:00
caller_name: PERRYVILLE   MD
caller_number: 4106425608
Wait failed -1, 10

直接使用脚本运行 Python 会产生预期的 0 返回码。

$ python3 ftc.py --caller_name "PERRYVILLE   MD" --caller_number 4106425608 --call_datetime "2019-04-04 18:07:00"
call_datetime: 2019-04-04 18:07:00
caller_name: PERRYVILLE   MD
caller_number: 4106425608
$ echo "$?"
0

我发现了两个类似的问题。第一个是Linux system() returns -1, ERRNO = 10 No child processes。问题中没有足够的信息,因此已关闭。第二个问题是system() returns -1, errno=10 when logged into Oracle。我认为这个问题不适用,因为 SIGCHLD 没有得到处理。

为什么wait 会以ECHILD 失败?


这是 C 程序。它调用 Python 脚本然后退出。

等待代码取自 wait(2) man page 示例。

$ cat test.c
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdint.h>
#include <pthread.h>
#include <spawn.h>
#include <sys/types.h>
#include <sys/wait.h>

#define log_error printf
#define log_info printf
extern char **environ;

int main(int argc, char* args[])
{
    char* const cname = "PERRYVILLE   MD";
    char* const cnumber = "4106425608";
    char* const ctime = "2019-04-04 18:07:00";

    char* const arguments[] = {
        "python3",
        "ftc.py",
        "--caller_name", cname,
        "--caller_number", cnumber,
        "--call_datetime", ctime,
        NULL
    };

    pid_t pid;
    int res = posix_spawn(&pid, "/usr/bin/python3", NULL, NULL, arguments, environ);

    if (res != 0) {
        log_error("Process failed %d, %d\n", res, errno);
        goto do_exit;
    } else {
        log_info("Process started, pid %d\n", pid);
    }

    do
    {
        res = waitpid(pid, &res, WUNTRACED | WCONTINUED);
        if (res == -1) {
            log_error("Wait failed %d, %d\n", res, errno);
            goto do_exit;
        }

        if (WIFEXITED(res)) {
            log_info("Process exited, result %d\n", WEXITSTATUS(res));
        //} else if (WIFSIGNALED(res)) {
        //    log_info("Process signaled, result %d\n", WTERMSIG(res));
        } else if (WIFSTOPPED(res)) {
            log_info("Process stopped, result %d\n", WSTOPSIG(res));
        } else if (WIFCONTINUED(res)) {
            log_info("Process continued\n");
        }
    } while (!WIFEXITED(res) /*&& !WIFSIGNALED(res)*/);

do_exit:

    return (int)WEXITSTATUS(res);
}

C 代码使用gcc -Wall -D_GNU_SOURCE -g3 -O1 -std=c99 -pthread test.c -o test.exe 编译。

这是 Python 脚本的相关部分。它打印脚本参数然后退出。

$ cat ftc.py
#!/usr/bin/env python3

import time
import sys

from selenium import webdriver
from selenium.webdriver.firefox.options import Options

def get_option(argv, option):

    argc = len(argv)
    for i in range(0, argc-1):
        if (argv[i] == option and i+1 < argc):
            return argv[i+1]

    return None

def main():

    caller_name = get_option(sys.argv, "--caller_name")
    caller_number = get_option(sys.argv, "--caller_number")
    call_datetime = get_option(sys.argv, "--call_datetime")

    if caller_name is None:
        sys.exit("caller_name is not available")
    if caller_number is None:
        sys.exit("caller_number is not available")
    if call_datetime is None:
        sys.exit("call_datetime is not available")

    print(f"call_datetime: {str(call_datetime)}")
    print(f"caller_name: {str(caller_name)}")
    print(f"caller_number: {str(caller_number)}")

    sys.exit(0)

if __name__ == "__main__":
    main()

【问题讨论】:

  • 我认为您的意思是 ECHILDerrno 值,而不是 SIGCHLD
  • 最好不要相信这些errno 映射编号,因为它们对于不同的平台可能不同。您最好将它们与本地定义进行比较,或者使用 perrorstrerror 之类的东西。
  • 使用goto do_exit; 和标签而不是使用break; 似乎很奇怪。不是您的问题的一个因素,而是不必要地使用goto
  • 您是否尝试过获取SIGCHLD 处理的状态?一个进程可以为SIGCHLD 继承SIG_IGN,这可能会解释你的结果。我想这意味着你的 shell 忽略了SIGCHLD。不可能,但是……因为福尔摩斯的格言值得一试:“一旦你消除了不可能的事情,剩下的,无论多么不可能,都必须是真相。”对此进行测试将消除不可能的事情,或者证明不可能的事情是真实的。
  • @Jonathan - goto 是真实代码的遗迹。真正的代码在线程中执行参数验证。失败时,goto 会跳过该特定块。然后标签处的代码删除了用于传递参数的malloc'd 结构,然后从线程代码中删除。

标签: python c wait


【解决方案1】:

您正在使用来自waitpid() 的返回值覆盖等待状态:

    res = waitpid(pid, &res, WUNTRACED | WCONTINUED);

对返回值和等待状态使用不同的变量:

    int ret = waitpid(pid, &res, WUNTRACED | WCONTINUED);
    if (ret == -1) {
        log_error("Wait failed %d, %d\n", ret, errno);
        goto do_exit;
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-13
    • 2021-07-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-06
    相关资源
    最近更新 更多