Start a process in the background in Linux with C

2020-02-12 05:24发布

问题:

I am trying to do something a little weird here. I need to start a process, logcat, from a deamon that will run in the background and print to the terminal without taking control of stdin. It is for logging so ideally logcat will print log messages while still allowing the user to input standard commands and initialize programs from the shell. Here is the code for the daemon I have so far. The program, logcat, starts and shows log messages but I cannot enter any commands into stdin as it appears that the program has taken control of stdin.

int main ( int argc, char** argv, char** env )
{
    int fd;
    if ((fd = open("/dev/console", O_RDWR)) < 0) {
        fd = open("/dev/null", O_RDWR);
    }
    printf("THIS IS A TEST\n");
    dup2(1, fd);
    dup2(2, fd);

    pid_t childpid = fork();

    if(childpid == -1) {
        perror("Failed to fork, logcat not starting");
        return 1;
    }

    if(childpid == 0) {
        //this is the child, exec logcat
        setsid();
        int execReturn = execl("/system/bin/logcat", "logcat", (char *) 0);
    } else {
        //this is the parent do nothing
        close(fd);
        return 0;
    }
    close(fd);
     return 0;
}

Thanks

回答1:

The 'logcat' command seems to be for Android development - that might explain the odd location of the command.

The key operation that you must fix is to ensure that you close your current standard input (the terminal) and open /dev/null/ for the input device:

close(0);
if ((fd = open("/dev/null", O_RDONLY)) != 0)
    ...error - failed to open /dev/null!

This means that your daemonized child process will not read anything from the terminal.


What I think you want to do is:

  1. Run your launcher program from a command line, which will have standard input, standard output and standard error connected to 'the terminal'.
  2. Inside your program, you want to replace the standard input so it comes from /dev/null.
  3. You want to leave standard output alone - you want logcat to write to the current standard output.
  4. You probably want to leave standard error alone too.

At some point in the proceedings, you do your daemonization properly (borrowing the link from @bstpierre's answer), making sure that the terminal you are connected to is not your controlling terminal, so that interrupts and hangups sent to the terminal don't affect your daemon. The plumbing is simpler than what you have set up - you should deal with standard input and leave standard output and standard error unchanged (instead of changing the outputs and leaving the input unchanged).

Now, you might want the output to go to /dev/console; if so, then it is reasonable to revise the code to open /dev/console. However, it is not reasonable to fall back on /dev/null if you can't open /dev/console; your program should report an error and fail (because there is no point in having logcat writing to /dev/null!). Make sure you open the console with the O_NOCTTY flag so it does not become the controlling terminal for the daemon.

The final comment I'd make is:

  • Are you sure you want random text appearing over your terminal or console when it is in use for other things?

I don't much like it when that happens.


See also: SO 958249



回答2:

How to Daemonize in Linux [dead link]

How to Daemonize in Linux [wayback machine archive of the above]

gist on github -- code taken from link above

Executive summary:

One of the things I keep running across is Linux daemons that don’t properly daemonize themselves. To properly daemonize, the following steps must be followed.

  • The fork() call is used to create a separate process.
  • The setsid() call is used to detach the process from the parent (normally a shell).
  • The file mask should be reset.
  • The current directory should be changed to something benign.
  • The standard files (stdin,stdout and stderr) need to be reopened.

Failure to do any of these steps will lead to a daemon process that can misbehave. The typical symptoms are as follows.

  • Starting the daemon and then logging out will cause the terminal to hang. This is particularly nasty with ssh.
  • The directory from which the daemon was launched remains locked.
  • Spurious output appears in the shell from which the daemon was started.


回答3:

There is special purposed function for this in glibc:

#include <unistd.h>

...
/* We are in the parent, yet */
daemon(0,0);
/* Now we are in the child */
...

More details here http://linux.die.net/man/3/daemon