【问题标题】:How can I make a linux background process (in c) that launches a Python script如何制作一个启动 Python 脚本的 linux 后台进程(在 c 中)
【发布时间】:2020-09-10 18:35:13
【问题描述】:

我创建了一个 Linux 后台进程(在 C++ 中),它监视一个目录并在某个文件出现在该目录中时尝试启动 Python 脚本。我的问题是负责启动 Python 脚本的子进程在调用 execvp 函数后立即退出,我不明白为什么。所有必要的文件都归根用户所有。如果有帮助,这是我的代码。提前感谢您的任何指点!我在发生错误的代码中标记了错误。我还包括了要调用的 Python 脚本

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

using namespace std;

char* arguments[3];
FILE* fd;
const char* logFilePath = "/home/BluetoothProject/Logs/fileMonitorLogs.txt";
char* rfcommPath = (char*)"/home/BluetoothProject/RFCOMMOut.py";
void logToFile(const char*);
void doWork();

void logToFile(const char* str) {
    fd = fopen(logFilePath, "a");
    fprintf(fd, "%s\n", str);
    fclose(fd);
}

int main() {
    arguments[0] = (char*)"python";
    arguments[1] = rfcommPath;
    arguments[2] = NULL;
    pid_t pid = fork();
    if(pid < 0) {
        printf("Fork failed");
        exit(1);
    } else if(pid > 0) {
        exit(EXIT_SUCCESS);
    }
    umask(0);
    pid_t sid = setsid();
    if(sid < 0) {
        logToFile("setsid() didn't work.");
        exit(1);
    }
    if ((chdir("/")) < 0) {
                logToFile("chdir() didn't work.");
                exit(EXIT_FAILURE);
        }
        close(STDIN_FILENO);
        close(STDOUT_FILENO);
        close(STDERR_FILENO);
    doWork();
}

void doWork() {
    pid_t pid = fork();
    if(pid < 0) {
        logToFile("doWork() fork didn't work.");
    } else if(pid > 0) {
        int status = 0;
        waitpid(pid, &status, 0);
        if(WEXITSTATUS(status) == 1) {
            logToFile("Child process exited with an error.");
        }
    } else {
        int error = execvp(arguments[0], arguments);   //Here is where the error is
        if(error == -1) {
            logToFile("execvp() failed.");
        }
        exit(1);
    }
}

Python 脚本 (AKA RFCOMMOut.py)

import RPi.GPIO as gpio
import serial
led_state = 0
led_pin = 11
gpio.setmode(gpio.BOARD)
gpio.setwarnings(False)
gpio.setup(led_pin, gpio.OUT)
try:
    ser = serial.Serial(port = '/dev/rfcomm0',
            baudrate = 9600,
            parity = serial.PARITY_NONE,
            stopbits = serial.STOPBITS_ONE,
            bytesize = serial.EIGHTBITS)
except IOException as e:
    logFile = open("/home/BluetoothProject/Logs/fileMonitorLogs.txt", "a")
        logFile.write("(First error handler) There was an exception:\n")
        logFile.write(str(e))
    logFile.write("\n")
        logFile.close()

#gpio.output

def process_input(input):
    global led_state
        if input == "I have been sent.\n":
                if led_state == 1:
            led_state = 0
            gpio.output(led_pin, led_state)
        else:
            led_state = 1
            gpio.output(led_pin, led_state)

while True:
    try:
        transmission = ser.readline()
        process_input(transmission)
    except IOError as e:
        logFile = open("/home/BluetoothProject/Logs/fileMonitorLogs.txt", "a")
        logFile.write("(second error handler) There was an exception:\n")
        logFile.write(str(e))
        logFile.write("\n")
        logFile.close()
        break
led_state = 0
gpio.output(led_pin, led_state)
gpio.cleanup()
print("End of program\n")

【问题讨论】:

  • 你为什么要在c做它并标记它c++?
  • 任何一种语言都可以接受
  • C 答案和 C++ 答案看起来会有些不同。既然您当前的代码大部分是用 C++ 编写的,为什么不坚持下去呢?旁注:包括头文件&lt;cstdlib&gt; 和&lt;cstdio&gt; 的C++ 版本。尝试创建minimal reproducible example - 它通常会更容易提供帮助。
  • 我会接受这个建议。谢谢你。我会看看是否可以将其编辑得更简洁

标签: python c++ linux daemon


【解决方案1】:

这个问题有点不清楚,所以我会尝试对问题所在进行一些不同的有根据的猜测,并分别解决每个问题。

TL;DR:删除 close(STDOUT_FILENO) 和 close(STDERR_FILENO) 以获取更多调试信息,这些信息有望为您指明正确的方向。

execvp(3) 返回 -1

根据execvp(3) 文档, execvp(3) 在失败时设置errno。为了理解它失败的原因,你的程序需要在某处输出 errno 的值;可能是标准输出、标准错误或您的日志文件。一个方便的方法是使用perror(3)。例如:

#include <stdio.h>
...
void doWork() {
  ...
  } else {
    int error = execvp(arguments[0], arguments);
    if(error == -1) {
      perror("execvp() failed");
    }
  }
  ...
}

如果不知道那个 errno 值是什么,就很难确定 execvp(3) 失败的原因。

execvp(3) 成功,但我的 Python 程序似乎没有运行

execvp(3) 成功意味着 Python 解释器已成功调用(假设您的 PATH 中没有名为“python”的程序,但实际上不是 Python 解释器)。如果您的程序似乎没有运行,这意味着 Python 难以加载您的程序。据我所知,在这种情况下,Python 总是会向 stderr 输出相关的错误信息;例如,如果 Python 找不到您的程序,它将向标准错误输出“No such file or directory”。

但是,看起来您的 C 程序在调用 doWork() 之前调用了 close(STDERR_FILENO)。根据fork(2),子进程继承父进程的open 文件描述符集的副本。这意味着在分叉之前调用close(STDERR_FILENO) 将导致子进程没有打开的stderr 文件描述符。如果 Python 在执行您的程序时出现任何错误,您永远不会知道,因为 Python 试图通过不存在的文件描述符通知您。如果 execvp(3) 成功并且 Python 程序似乎根本没有运行,那么我建议您从 C 程序中删除 close(STDERR_FILENO) 并再次运行所有程序。在没有看到 Python 输出的错误信息的情况下,很难确定 Python 程序运行失败的原因。

顺便说一句,我建议不要显式关闭 stdin、stdout 和 stderr。根据stdin(3),标准流通过调用 exit(3) 和正常程序终止来关闭。

execvp(3) 成功,我的 Python 程序正在运行,但我的 Python 程序在执行任何有用的工作之前就退出了

在这种情况下,我不确定问题可能是什么,因为我对 Raspberry Pi 不是很熟悉。但我认为如果在运行 Python 程序之前不关闭标准流,调试会更轻松。

希望这会有所帮助。

【讨论】:

  • 非常感谢!我为我含糊/不完整的问题道歉。我不经常在堆栈溢出上发帖。我的大部分问题已经被其他帖子回答了。在旁注中,您能否指出我正确的方向来建立我在 c 或 c++ 上的知识?我更喜欢用这两种语言中的任何一种来编写系统代码,因为它们的开销很低,并且相对于 Java 这样的语言对操作系统来说效率很高。
  • IMO,学习一门编程语言的最好方法是亲身体验它;所以我建议考虑编写程序,或者采用您以前用其他语言编写的程序,然后用 C 或 C++ 编写它们。我个人喜欢编写国际象棋程序来学习一门新语言;我认为国际象棋有足够有趣的界面设计问题来很好地掌握一门新语言。话虽如此,请考虑您是否正在尝试学习语言或学习技术。我想说这个特定的问题更像是一个 GNU/Linux 问题而不是 C/C++ 问题。
  • 我想我会采纳你的建议。再次感谢这是一个很大的帮助!祝你有美好的一天
猜你喜欢
  • 2011-03-25
  • 2011-04-05
  • 1970-01-01
  • 2014-05-04
  • 2013-10-14
  • 1970-01-01
  • 1970-01-01
  • 2013-09-02
  • 1970-01-01
相关资源
最近更新 更多