【问题标题】:In C, is there a way to pass a return value of one program to another program?在 C 中,有没有办法将一个程序的返回值传递给另一个程序?
【发布时间】:2021-01-11 01:40:05
【问题描述】:

自从我是 C 的新人以来,我有 2 个基本问题:

  1. 如何将 C 程序的返回值传递给另一个程序? prog1 将列出项目(每次执行时项目的数量可能会有所不同),我想只存储最后一个项目值并将其传递给另一个 prog2 以用于不同的目的。基本上 prog1 的输出在下面,我想提取列表中的最后一项,即 prog2 的 /dev/hidraw2。 Prog2 目前正在使用硬编码值,我想让它更具动态性。

prog1 输出:

/dev/hidraw0
/dev/hidraw1
/dev/hidraw2

prog1 代码可以在这里找到:

https://pastebin.pl/view/379db296

prog2 代码 sn-p 如下:

/* C */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

const char *bus_str(int bus);

int main(int argc, char **argv)
{
        int fd;
        int i, res, desc_size = 0;
        char buf[256];
        struct hidraw_report_descriptor rpt_desc;
        struct hidraw_devinfo info;
        char *device = "/dev/hidraw2";   /*** replace hardcoded value here dynamically ***/

        if (argc > 1)
                device = argv[1];

        /* Open the Device with non-blocking reads. In real life,
           don't use a hard coded path; use libudev instead. */
        fd = open(device, O_RDWR);
  1. 如果解决了上面的问题 1,我可以直接将 prog1 合并到 prog2 中,即使它们具有不同的编译参数?您认为那里有什么问题吗?

prog1 编译命令:
gcc -Wall -g -o prog1 prog1.c -ludev

prog2 编译命令:
gcc prog2 prog2.c

【问题讨论】:

  • 在不修改 prog1 的情况下,我相信您的 prog2 必须通过生成一个进程并读取输出来运行 prog1...我建议您看看这个:geeksforgeeks.org/named-pipe-fifo-example-c-program
  • 你真的需要让 prog2 运行 prog1 吗?根据您运行它的方式以及您使用的 OS/shell,最简单的解决方案可能是更新 prog2 以从标准输入读取它的输入,然后使用类似 ./prog1 | tail -n 1 | ./prog2 的东西运行它(其中 tail -n 1 是限制到最后一行,这样 prog2 就不需要这样做了)。
  • 它们真的必须是单独的程序吗?
  • C不知道IPC的概念。许多使用 C 的平台没有 IPC 或进程的概念。对于类似 unix 的操作系统,我建议使用套接字或通过 @EdmCoff 建议的 stdout/stdin 管道
  • 编译标志对于组合它们并不是真正的问题。我建议您启用所有不想显式禁用的警告(使用-Wall -Wextra -Wpedantic)。链接库不是问题 (-ludev)。调试器信息对两者都不是问题,使用它(-g)。

标签: c function cross-compiling hid libudev


【解决方案1】:

一种方法,也有很多,就是使用 fork。 fork() 与其他 C 函数调用有点不同。当你调用它时,你会得到一份正在运行的进程的额外副本。然后可以比较返回值来确定哪个进程是父进程,哪个进程是子进程。

整体逻辑看起来有点像:

while (looping) {
   char device[] = (set to the desired value);
   int childPID = fork();
   if (childPID == 0) {
       /* this is the child */
       doWhatever(device);
       exit(0);
   } else {
       /* this is the parent */
       /* let's wait on the child to complete */
       wait();           
   }

现在这个例子很简单。还有很多其他的考虑;但是这种方法避免了在进程之间传递信息的其他更复杂的方法。基本上,子进程在调用fork() 的那一刻被创建为程序的副本,因此device 的内容将在两个进程之间共享。

需要注意的一些问题是子进程需要向父进程报告其退出代码。因此,如果您不想一次运行一个进程,则需要一种更复杂的方法来wait() 子级。

如果你在这个例子中杀死父进程,子进程也会被杀死。这可能是也可能不是您想要的。

其他方法(有些比其他方法好得多):

  1. 使用 IPC 创建基于共享内存和信号量的生产者/消费者模式(以控制谁在写入共享内存,因此读取进程不会读取部分写入的条目或删除部分会导致写入器出现问题的项目。
  2. 使用上述方法(fork),但在子模式中创建一个未命名的管道,然后子进程从中读取。这可以允许子进程处理多个请求。
  3. 使用独立于两个程序存在的命名管道(一种非常特殊的文件)。这允许独立启动子项。
  4. 使用由生产者编写并由消费者读取的常规文件。这将需要某种信号来确定文件何时可以安全写入(因此消费者不会得到错误的读取)以及文件何时可以安全地读取/缩短(因此写入者不会损坏文件)。李>

我相信还有很多其他方法;但是,当你有两个程序合作解决一个问题时,你会开始遇到很多只考虑一个程序解决问题时通常不会遇到的问题。

需要考虑的一些事情:

  1. 通信是可靠的 - 文件、套接字、网络等有时都会失败。您需要验证您的发送是否已发送,并提供一些方法来了解您的数据没有因传输而损坏。

  2. 通信行为需要时间——您需要处理传输的打包、插入、检索和解包过程中的延迟;而且,这些延迟通常会因每条消息而发生巨大变化。

  3. 可以处理的消息数量是有限的 - 传输需要时间,而时间意味着传输速率。由于涉及到速率,因此您不能设计一个忽略通信路径限制的工作程序。

  4. 消息可能被破坏或窃听 - 虽然我们喜欢认为计算机永远不会出错,但在通信数据中的错误更频繁地发生。这可能是由于很多原因(人们使用 telnet 测试套接字、对网络的电气干扰、管道文件被删除等)。此外,数据本身更容易被其他人读取,这对某些程序来说是个问题。

  5. 传输方式发生变化 - 即使您使用文件而不是网络来传输信息,管理员也可以移动文件并插入符号链接。网络可能会通过不同的路径发送每一位信息。不要假设静态路径。

  6. 您无法提供避免关键问题的说明 - 没有计算机只有一名管理员,即使它是您自己的个人计算机。自动化系统和子程序,以及其他人,确保您永远无法向所有合适的人提供如何解决问题的说明。编写您的解决方案,避免需要人们按照自定义“必需”程序实施的变通办法。

  7. 移动数据不是免费的——它需要时间、电力、RAM、CPU,还可能需要磁盘或网络。即使解决方案的所有其他部分都是正确的,这些成本也会增加(如果不加以管理)以阻止您的程序运行。

  8. 数据的传输通常不是同质的 - 一旦您采用一种信息交流方式,很可能不容易用另一种方式轻松替换它。许多解决方案提供了其他方法中没有的附加功能,即使您决定使用“仅网络”传输,网络之间的差异也可能使您的解决方案不像您想象的那样通用。

有了这些认识,您将更容易创建一个有效的解决方案,并且不会在一些微小的细节发生变化时崩溃。

【讨论】:

    猜你喜欢
    • 2021-06-10
    • 2013-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多