22

In a Linux-based project that I am working on, I need to be able to find all of my child processes. It is not feasible to record every time one is started -- they need to be found after the fact. This needs to be pure C, and I'd like to do it without reading /proc. Does anyone know how to do this?

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
c4757p
  • 1,728
  • 4
  • 18
  • 25
  • By 'pure C' I was sort of including 'not parsing command output'. – c4757p Jun 17 '09 at 21:29
  • 1
    Why is it not feasible? This is by far the cleanest and most efficient way to do it. – Duck Jun 17 '09 at 21:31
  • Are you opposed to writing an LKM that will override one of the unused system calls? If so, it'd be trivial to write a small system call that'll do it for you. – FreeMemory Jun 18 '09 at 14:57
  • An LKM is actually a pretty good idea. I guess I left out -- this is for a very low-level program, that must be able to get this info when it is one of the only things functional on a booting system. That's why I don't want to call 'ps'. Not quite sure what my aversion to reading /proc was; I'll try both an LKM and reading /proc and see what works better. – c4757p Jun 21 '09 at 13:42
  • @Duck Jun: I wanted to get all the children of the parent process of the current process (to transverse all processes like `ps` does), in which case it is not feasible. True, in this question, depends on what *my child processes* mean: children of current process, or children of an arbitrary process owned by the current user. Sounds more like the former in this question. – Ciro Santilli OurBigBook.com Jul 11 '13 at 07:40

8 Answers8

6

It is usually entirely feasible to record child processes every time you start one. conveniently, the parent process is passed the pid value of the child process as the return value of the fork call which creates it.

As the man page says:

pid_t fork(void);

It would help if you could tell us why you think it isn't feasible.

Alex Brown
  • 41,819
  • 10
  • 94
  • 108
  • I assumed he had a good reason. But you're right. It's usually pretty trivial to get that information. – sangretu Jun 17 '09 at 21:51
  • I know this is an old thread, but here's a use case where this is not feasible: Your forked child `exec`s some other process (maybe user-specified), and you don't know how many children that may spawn. – anroesti Oct 04 '22 at 00:15
5

You could use popen

Something like. (Hopefully the syntax is close enough)

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

int main(int argc, char *argv[])
{
    FILE *fp = popen("ps -C *YOUR PROGRAM NAME HERE* --format '%P %p'" , "r");
    if (fp == NULL)
    {
        printf("ERROR!\n");
    }

    char parentID[256];
    char processID[256];
    while (fscanf(fp, "%s %s", parentID, processID) != EOF)
    {
         printf("PID: %s  Parent: %s\n", processID, parentID);

         // Check the parentID to see if it that of your process
    }

    pclose(fp);

    return 1;
}


RC.
  • 27,409
  • 9
  • 73
  • 93
  • bleh, same answer as sangretu's. – Byron Whitlock Jun 17 '09 at 21:58
  • Byron, I voted you up because you probably have the best answer for what the question is now that ps is not an option. Though, I do hope you did not vote me down b/c my answer is the same as sangretu's with more detail. We were 2 minutes apart and it took me time to write the initial pseudo-code. I have since edited to provide more detailed code that works. – RC. Jun 17 '09 at 22:02
5

I find your comment that it is not feasible to record the creation of processes to be odd, but if you really can't (possibly because you don't know how many will be created and don't want to have to keep reallocing memory), then I would probably open all of the files that match the glob /proc/[1-9]*/status and look for the line that says PPid: <num> where <num> was my process id.

Chas. Owens
  • 64,182
  • 22
  • 135
  • 226
  • And then he/she accepted my answer, so I guess he/she changed his/her mind after seeing how easy it would be. – Chas. Owens Jul 21 '09 at 21:24
  • 11
    One perfectly fair reason why you might not be able to record the creation of every child process: if you know that a library forks processes, and it doesn't give you their PIDs. – tomjakubowski Sep 12 '16 at 16:50
  • can you please elaborate your answer? I'd i get the child of particular process?? – Waseem Ahmad Naeem Oct 01 '17 at 08:42
  • 2
    The comment is not so odd. It is not feasible to record creation of processes if your process is a subreaper (PR_SET_CHILD_SUBREAPER) and your grandchildren are promoted to children. You could track creation it with ptrace but the performance hit can be drastic. – cheshirekow Nov 06 '19 at 21:09
4

You could try this

#include<string.h>
#include <sys/types.h>
#include <unistd.h>

char str[50] = "ps -o pid --ppid ";
char ppid [7];
sprintf(ppid,"%d",getpid());
strcat(str,ppid);
system(str);

NOTE: This piece of code needs to be in the parent process

Basically ps -o pid --ppid <parent_id> gives the pid of all child processes whose parent has PID <parent_id>. Now, we can get the parent's process's PID by using getpid(), which returns pid_t and is implicitly converted to an integer. sprintf() converts it into a string and we concatenate the result with str to get the complete command which is executed by system().

Til
  • 5,150
  • 13
  • 26
  • 34
gokul_uf
  • 740
  • 1
  • 8
  • 21
  • Does `system(str)` show output at console & I also want to that all ids in an array – Waseem Ahmad Naeem Oct 01 '17 at 08:44
  • Note that `ps -o pid --ppid` will also return its own pid as the last pid in the list. – vasilyrud May 18 '18 at 19:14
  • Much shorter: `char str[30]; /* pid only */ sprintf(str, "ps -o pid= --ppid %d", getpid()); system(str); /* name only */ sprintf(str, "ps -o comm= --ppid %d", getpid()); system(str); /* pid and name */ sprintf(str, "ps -o pid= -o comm= --ppid %d", getpid()); system(str); ` – Ronny Sherer Aug 08 '22 at 13:01
1

You could parse a process list (ps -ax?) that included the parent process ID. This could probably be done with a simple shell script.

sangretu
  • 1,208
  • 9
  • 12
1

If you're trying to get all child processes for the very specific purpose of waiting for them to exit, you can use waitpid(-1, ...):

while (true) {
  // Wait for any child exiting
  int child_status;
  const int child_pid = waitpid(-1, &child_status, 0);
  // check child_status
}
Daryl
  • 3,253
  • 4
  • 29
  • 39
  • I can use your solution for showing background process's status in my shell. But, when the loop break? – alhelal Aug 23 '17 at 13:26
0

If you want to trace fork events and extract child pids for debugging purposes, there are a number of ways to do that, including:

  • Using GDB
  • using strace
  • Using systemtap
  • Using kernel event connectors (not sure what these are exactly)
Alex Brown
  • 41,819
  • 10
  • 94
  • 108
0

The code in this repository will need to be adjusted to be written in plain C to meet your request, and if you are ok with doing so, if you remove all the unused functionality from my repository within your project, it shouldnt be that hard to translate into plain C.

But this is more useful for people looking to support multiple platforms and/or who prefer C++.

See the function pids_from_ppid(ppid) https://github.com/time-killer-games/enigma-dev/blob/548dc16e96a2a32f8ad9045a4ee18b0206516e62/ENIGMAsystem/SHELL/Universal_System/Extensions/ProcInfo/procinfo.h#L101

Returns a string with each child process id separated by a pipe "|" character as the delimiter.

Ubuntu and debian users need libprocps-dev installed for missing headers.

sudo apt-get install libprocps-dev libx11-dev

The libx11-dev dependency is optional because all X11 code in the source can be omitted and the question here will still be answered, so if you need wayland and no X11 support you should remove X11 related code as it's unrelated to this question anyway.

For those who actually like/need X11 and/or C++ this will work out of the box on Windows, Mac, Linux, and FreeBSD. Support for other BSD's is not as easily viable thanks to relying on the libutil dependency. But it just uses sysctl() internally so in theory you should be able to compile the source code for libutil on the other BSD's available from FreeBSD's github repository, and then link to it after you have built it.