简体   繁体   中英

In C, is there a way to pass a return value of one program to another program?

I have 2 basic questions since I'm new at C:

  1. How to go about passing the return value of a C program to another program? prog1 will list out items (number of items can be varied each time of execution) and I'd like to store and pass ONLY the last item value to another prog2 for different purpose. Basically the output of prog1 is below and I'd like to extract the last item on the list which is /dev/hidraw2 for the prog2. Prog2 is currently using hardcoded value and I'd like to make it more dynamic.

prog1 output:

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

prog1 code can be found here:

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

prog2 code snippet is below:

/* 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. If question 1 above is resolved, can I incorporate prog1 into prog2 directly even though they have different compilation parameters? Do you forsee any issue there?

prog1 compile command:
gcc -Wall -g -o prog1 prog1.c -ludev

prog2 compile command:
gcc prog2 prog2.c

One way, and there are many, is to use fork. fork() is a little unlike other C function calls. When you call it, you get an extra copy of the running process. You can then compare the return value to determine which process is the parent and which process is the child.

The overall logic would then look a little like:

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();           
   }

Now this example is very simple. There are many other considerations; but this approach avoids other more complex ways to pass information between processes. Basically the child is created as a copy of the program at the moment in time that fork() is called, so the contents of device will be shared between the two processes.

Some of the problems to watch out for is that children need to report their exit codes to their parent processes. So if you don't want to have one process running at a time, you'll need a more complex way to wait() on the children.

If you kill the parent process in this example, the children will also be killed. That may or may not be what you desire.

Other approaches (some much better than others):

  1. Using IPC to create a producer / consumer pattern based on shared memory and semaphores (to control who's writing to the shared memory, so reading processes don't read partially written entries or remove items partially creating problems with writers.
  2. Using the above approach (fork) but creating an unnamed pipe in the patern that the child then reads from. This can permit the child to process more than one request.
  3. Using a named pipe (a very special kind of file) that exists independently of the two programs. This allows independent launching of the child(ren).
  4. Using a regular file, written by the producer and read by the consumer(s). This will require some sort of signalling for when the file is safe to write to (so consumers don't get bad reads) and when the file is safe to read / shorten (so the writers don't corrupt the file).

I'm sure there are many other approaches; but, when you have two programs cooperating to solve a problem, you start to encounter a lot of issues that you don't normally have when only considering one program solving the problem.

Some of the things to consider:

  1. The communication is reliable - files, sockets, networks, etc. all sometimes fail. You need to verify your sends were sent and provide some means for knowing your data is not corrupt due to the transport.

  2. The act of communicating requires time - you will need to handle delays in both the packaging, insertion, retrieval, and unpackaging of the transmission; and, often those delays can change dramatically for each message.

  3. The number of messages that can be handled is finite - transmission requires time, and time implies a rate of transmission. Since there is a rate involved, you cannot design a working program that ignores the limits of the communication path.

  4. The message can be corrupted or eavesdropped - While we like to think that computers never make errors, when communicating errors in the data occur more frquently. These can be due to lots of reasons (people testing sockets with telnet, electrical interference with networks, pipe files being removed, etc.) In addition, the data itself is more easily read by others, which is a problem for some programs.

  5. The way the transmission occurs changes - Even if you are using files instead of networks to transmit information, administrators can move files and insert symlinks. Networks may send every bit of information through a different path. Don't assume a static path.

  6. You can't provide instructions to avoid critical issues - No computer has only one administrator, even if it is your own personal computer. Automated systems and subroutines, as well as other people, ensure that you'll never be able to provide instructions on how to work around issues to all the right people. Write your solutions avoiding a need for workarounds that are implemented by people following a custom "required" procedure.

  7. Moving data is not free - it costs time, electricity, RAM, CPU, and possibly disk or network. These costs can grow (if not managed) to prevent your program from functioning, even if all other parts of the solution are correct.

  8. Transportation of data is often not homogenous - Once you commit to a way of communicating information, odds are that it will not be easy to replace it with another way easily. Many of the solutions provide additional features that aren't present in other approaches, and even if you decide on "network only" transport, the difference between networks may make your solution less generic that you might think.

Armed with these realizations, it will be much easier for you to create a solution that works, and doesn't fall apart when some tiny detail changes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM