:: Re: [DNG] [OT] files disappearing r…
Top Page
Delete this message
Reply to this message
Author: Steve Litt
Date:  
To: dng
Subject: Re: [DNG] [OT] files disappearing reproducibly
Florian Zieboll via Dng said on Sat, 19 Feb 2022 18:51:54 +0100


>Back to the keyboard, I just discovered, that every (GUI) program I
>run, is spawned from PID 1.


This is not precisely true. Even though ps ax lists the PPID of the
programs you run is 1, PID1 didn't spawn them. Here's the explanation...

When there's a desire to background a program in such a way that you
can close the parent program without closing the "spawned" program,
without using the problematic nohup, the iconic way to do it is called
"doublefork", which *assigns* the PPID to PID1.

As a doublefork example, the following is the C code my qownbackup
system uses to background the `qownbackup backup` command:

==============================================================
#include<unistd.h>
#include<stdio.h>

int main(int argc, char *argv[]){
    pid_t pid = fork();
    if(pid < 0){
        printf("First fork failed.\n");
        perror("First fork failed!");
    } else if(pid == 0){
        pid = fork();
        if(pid < 0){
            printf("Second fork failed.\n");
        perror("Second fork failed!");
        } else if(pid == 0){
            execl("./qownbackup.sh", "./qownbackup.sh", "backup", NULL);
        }
    }
}
==============================================================


Aitor can correct any misunderstandings I have, but here goes my
explanation...

First, understand that the fork() function produces a second process,
called "the child", almost completely identical to the first. The
child's program counter is the same as the parent's, so both process
proceed from the point immediately after the fork() function.

The fork() function returns one of three ranges of integers:

* Less than 1 on error.
* Zero if the current process is the child.
* Greater than zero, and in fact the child's PID, if the process is
the parent.

The child of a child is assigned a PPID of 1.

So the first thing is to do one fork, test the return code for error,
and abort if so. If the return code is positive, indicating that this
is the parent, do nothing, so that the parent finishes normally.

But if the return code is zero, this is the first child, so do another
fork. Once again, run the fork() function, and if the return indicate's
that you're a child (of the child the child the original created), do
an exec function to replace yourself with the command you want to
background, in this case "qownbackup backup".

Because it's the child of a child, its PPID is 1. Aitor, I don't think
I've given the complete story, I think there's a dependency on which
process finishes first, and as such I might have a race condition in my
code.

Also, I think I should have done a setsid() after the second fork, and
I should have done something to close stdin, and redirect stdout and
stderr, to a log file or to /dev/null.

The following is the (Python3) doublefork used in my UMENU2 software:

==============================================================
#!/usr/bin/python

import sys
import os

if os.fork():
    sys.exit(0)
if os.fork():
    sys.exit(0)
os.setsid()  ### New to 1.9.3, guarantees complete fork
sys.argv.pop(0)
executable = sys.argv[0]
os.execvp(executable, sys.argv)
==============================================================


In the preceding, errors aren't handled, so it just does two straight
forks and proceeds only if both return zero. Then it does a setsid(),
which I think my C implementation should have done too, and then exec's
the desired command, just like my C implementation does.

Doubleforking works in pretty much any computer language with the
fork(). You can even do it in bash or dash, but it's not so
straightforward.

SteveT

Steve Litt
March 2022 featured book: Making Mental Models: Advanced Edition
http://www.troubleshooters.com/mmm