If I Have a Process, and I Clone It, Is the Pid the Same

If I have a process, and I clone it, is the PID the same?

Not quite. If you clone a process via fork/exec, or vfork/exec, you will get a new process id. fork() will give you the new process with a new process id, and exec() replaces that process with a new process, but maintaining the process id.

From here:

The vfork() function differs from
fork() only in that the child process
can share code and data with the
calling process (parent process). This
speeds cloning activity significantly
at a risk to the integrity of the
parent process if vfork() is misused.

Within child and parent processes, variables has same addresses but have different value?

That is how fork works. It makes a copy of the address-space, actually it makes some optimizations (only changed pages have to be written).

The copy uses the same addresses:

  • this is possible because the parent and child process will each only see their version

  • and it is required: if you have any linked datastructure, the pointers from one object to another have to remain valid. If the copied address space would occupy different addresses then all pointers would have to be fixed - and the operating system does not even know which of the bytes are pointers and which are just data.

Strange behavior of clone

You have a race condition (i.e.) you don't have the implied thread safety of stdio.

The problem is even more severe. You can get duplicate "func" messages.

The problem is that using clone does not have the same guarantees as pthread_create. (i.e.) You do not get the thread safe variants of printf.

I don't know for sure, but, IMO the verbiage about stdio streams and thread safety, in practice, only applies when using pthreads.

So, you'll have to handle your own interthread locking.

Here is a version of your program recoded to use pthread_create. It seems to work without incident:

#define _GNU_SOURCE

#include <sched.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>

#define STACK_SIZE 1024*1024

void *func(void* param) {
printf("I am func, pid %d\n", getpid());
return (void *) 0;
}

int main(int argc, char const *argv[]) {
printf("I am main, pid %d\n", getpid());
void* ptr = malloc(STACK_SIZE);

printf("I am calling clone\n");

pthread_t tid;
pthread_create(&tid,NULL,func,NULL);
//int res = clone(func, ptr + STACK_SIZE, CLONE_VM, NULL);
int res = 0;

// works fine with sleep() call
// sleep(1);

if (res == -1) {
printf("clone error: %d", errno);
} else {
printf("I created child with pid: %d\n", res);
}

pthread_join(tid,NULL);
printf("Main done, pid %d\n", getpid());
return 0;
}

Here is a test script I've been using to check for errors [it's a little rough, but should be okay]. Run against your version and it will abort quickly. The pthread_create version seems to pass just fine

#!/usr/bin/perl
# clonetest -- clone test
#
# arguments:
# "-p0" -- suppress check for duplicate parent messages
# "-c0" -- suppress check for duplicate child messages
# 1 -- base name for program to test (e.g. for xyz.c, use xyz)
# 2 -- [optional] number of test iterations (DEFAULT: 100000)

master(@ARGV);
exit(0);

# master -- master control
sub master
{
my(@argv) = @_;
my($arg,$sym);

while (1) {
$arg = $argv[0];
last unless (defined($arg));

last unless ($arg =~ s/^-(.)//);
$sym = $1;

shift(@argv);

$arg = 1
if ($arg eq "");

$arg += 0;
${"opt_$sym"} = $arg;
}

$opt_p //= 1;
$opt_c //= 1;
printf("clonetest: p=%d c=%d\n",$opt_p,$opt_c);

$xfile = shift(@argv);
$xfile //= "clone1";
printf("clonetest: xfile='%s'\n",$xfile);

$itermax = shift(@argv);
$itermax //= 100000;
$itermax += 0;
printf("clonetest: itermax=%d\n",$itermax);

system("cc -o $xfile -O2 $xfile.c -lpthread");
$code = $? >> 8;
die("master: compile error\n")
if ($code);

$logf = "/tmp/log";

for ($iter = 1; $iter <= $itermax; ++$iter) {
printf("iter: %d\n",$iter)
if ($opt_v);
dotest($iter);
}
}

# dotest -- perform single test
sub dotest
{
my($iter) = @_;
my($parcnt,$cldcnt);
my($xfsrc,$bf);

system("./$xfile > $logf");

open($xfsrc,"<$logf") or
die("dotest: unable to open '$logf' -- $!\n");

while ($bf = <$xfsrc>) {
chomp($bf);

if ($opt_p) {
while ($bf =~ /created/g) {
++$parcnt;
}
}

if ($opt_c) {
while ($bf =~ /func/g) {
++$cldcnt;
}
}
}

close($xfsrc);

if (($parcnt > 1) or ($cldcnt > 1)) {
printf("dotest: fail on %d -- parcnt=%d cldcnt=%d\n",
$iter,$parcnt,$cldcnt);
system("cat $logf");
exit(1);
}
}

UPDATE:

Were you able to recreate OPs problem with clone?

Absolutely. Before I created the pthreads version, in addition to testing OP's original version, I also created versions that:

(1) added setlinebuf to the start of main

(2) added fflush just before the clone and __fpurge as the first statement of func

(3) added an fflush in func before the return 0

Version (2) eliminated the duplicate parent messages, but the duplicate child messages remained

If you'd like to see this for yourself, download OP's version from the question, my version, and the test script. Then, run the test script on OP's version.

I posted enough information and files so that anyone can recreate the problem.

Note that due to differences between my system and OP's, I couldn't at first reproduce the problem on just 3-4 tries. So, that's why I created the script.

The script does 100,000 test runs and usually the problem will manifest itself within 5000-15000.

The difference between fork(), vfork(), exec() and clone()


  • vfork() is an obsolete optimization. Before good memory management, fork() made a full copy of the parent's memory, so it was pretty expensive. since in many cases a fork() was followed by exec(), which discards the current memory map and creates a new one, it was a needless expense. Nowadays, fork() doesn't copy the memory; it's simply set as "copy on write", so fork()+exec() is just as efficient as vfork()+exec().

  • clone() is the syscall used by fork(). with some parameters, it creates a new process, with others, it creates a thread. the difference between them is just which data structures (memory space, processor state, stack, PID, open files, etc) are shared or not.

How can _do_fork() return two different PIDs (one for the parent process and one for the child process)

You have correctly identified how the new process id is returned to the parent, with return nr. But you will never actually see a return 0 anywhere since this code is executed on the parent thread. This code is not for the new process that is created.

Now let us examine the _do_fork function.

...
}
p = copy_process(clone_flags, stack_start, stack_size,
child_tidptr, NULL, trace, tls, NUMA_NO_NODE);
add_latent_entropy();
...

This is where all the magic happens. When you call copy_process
, it internally calls copy_thread which is a target specific code. This function is responsible for coping the thread related data structures.

Now say we have the target as X86_64 with the calling convention that the return value is returned in the %rax register. This function then copies 0 into %rax and copies the value of return_from_fork address to %rip(the instruction pointer).

On other platforms the ABI might require the return value to go on the stack. In that case 0 is placed on the stack. copy_thread is target specific but copy_process is not.

This is the implementation of copy_thread for X86_64. You can see around line number 160 the sp registers being set. And at line 182 you can see %ax (which is a subregister of %rax) being set to 0.

I hope this clears some confusion.

Relation between Thread ID and Process ID

I got the answer here on stackoverflow. It states that if we run a program on Linux that contains the libc libuClibc-0.9.30.1.so (1). Basically an older version of libc then thread created will have different PID as shown below

root@OpenWrt:~# ./test
main thread pid is 1151
child thread pid is 1153

and I tried to run this program with a linux that contains the libc from ubuntu libc6 (2) i.e newer version of libc then Thread created will have the same PID as the process.

$ ./test
main thread pid is 2609
child thread pid is 2609
The libc (1) use linuxthreads implementation of pthread

And the libc (2) use NPTL ("Native posix thread library") implementation of pthread

According to the linuxthreads FAQ (in J.3 answer):

each thread is really a distinct process with a distinct PID, and signals sent to the PID of a thread can only be handled by that thread

So in the old libc which use linuxthreads implementation, each thread has its distinct PID

In the new libc version which use NPTL implementation, all threads has the same PID of the main process.

The NPTL was developed by redhat team. and according to the redhat NPTL document: One of the problems which are solved in the NPTL implementation is:

(Chapter: Problems with the Existing Implementation, page5)

Each thread having a different process ID causes compatibility problems with other POSIX thread implementations. This is in part a moot point since signals can't be used very well but is still noticeable

And that explain this issue.

I am using the new libc version that contains the NPTL ("Native posix thread library") implementation of pthread.

Get pid from brother process

The parent should fork three times, the children should not fork. This way, the parent will know the pids of all three children.

After the fork, you'll need some kind of separate communication channel by which the parent can communicate these pids to all children. A simple way would be to open a pipe (see pipe(2)) before forking each child, so the child inherits the pipe's file descriptor (at least the read end) and the parent keeps the write end. Then have the parent send the three pids down each pipe and close it.

Example code (long, but that's the nature of C):

#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#define NUM_CHILDREN 3

/* Entry point for the child processes */
int child_main(int pipe_read_end) {
pid_t my_pid = getpid();

/* Read child pids from pipe */
int child_pids[NUM_CHILDREN];
unsigned int bytes_read = 0;
while (bytes_read < sizeof(child_pids)) {
ssize_t result = read(pipe_read_end, ((unsigned char *) child_pids) + bytes_read, sizeof(child_pids) - bytes_read);
if (result < 0) {
perror("error reading from pipe");
return 1;
} else if (result == 0) {
fprintf(stderr, "unexpected end of file\n");
return 1;
} else {
bytes_read += result;
}
}
close(pipe_read_end);

/* Do something useful with these child pids */
for (int i = 0; i < NUM_CHILDREN; i++) {
printf("Child %d received sibling pid %d\n", my_pid, child_pids[i]);
}

return 0;
}

/* Entry point for the parent process. */
int main() {
int child_pids[NUM_CHILDREN];
int pipe_write_ends[NUM_CHILDREN];
for (int i = 0; i < NUM_CHILDREN; i++) {
/* Create the pipe for child i */
int pipefd[2];
if (pipe(pipefd)) {
perror("error creating pipe");
return 1;
}
int pipe_read_end = pipefd[0];
int pipe_write_end = pipefd[1];

/* Fork child i */
pid_t child_pid = fork();
if (child_pid < 0) {
perror("error forking");
return 1;
} else if (child_pid == 0) {
printf("Child %d was forked\n", getpid());
close(pipe_write_end);
return child_main(pipe_read_end);
} else {
printf("Parent forked child %d\n", child_pid);
close(pipe_read_end);
pipe_write_ends[i] = pipe_write_end;
child_pids[i] = child_pid;
}
}

/* Send pids down the pipes for each child */
for (int i = 0; i < NUM_CHILDREN; i++) {
unsigned int bytes_written = 0;
while (bytes_written < sizeof(child_pids)) {
ssize_t result = write(pipe_write_ends[i], ((unsigned char *) child_pids) + bytes_written, sizeof(child_pids) - bytes_written);
if (result < 0) {
perror("error writing to pipe");
return 1;
} else {
bytes_written += result;
}
}
close(pipe_write_ends[i]);
}

/* Wait for children to exit */
for (int i = 0; i < NUM_CHILDREN; i++) {
if (waitpid(child_pids[i], 0, 0) < 0) {
perror("error waiting for child");
return 1;
}
}
}

As @PSkocik points out in their answer, you should probably not be doing this. Pids can be reused by the OS, so there's no way for the children to know that their sibling pids still actually refer to their siblings; only the parent can be sure, because it has to wait for each pid before it can be reused.

However, this same mechanism can be used for other forms of IPC (inter-process communication); you could, for example, use it to create pipes between the children directly.

Can a process have two PIDs?

From fork man page :

Return Value

On success, the PID of the child process is returned in the parent,
and 0 is returned in the child. On failure, -1 is returned in the
parent, no child process is created, and errno is set appropriately.

Fork does not returns the pid of the child, only in the parent. Therefore, the child process does not have two pids.

Try this

int main(int argc, const char * argv[])
{
pid_t thisPid, parentPid, newPid;
int count = 0;
thisPid = getpid();
parentPid = getppid();

printf("thisPid = %d, parent pid = %d\n", thisPid, parentPid);

if ((newPid = Fork()) == 0) {
count++;
printf("I am teh child. My pid is %d\n", getpid());
exit(0);
}
else
printf("I am the parent. My pid is %d, my child pid is %d\n", thisPid, newPid);
return 0;
}


Related Topics



Leave a reply



Submit