Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C, exit and pcntl_wait functions produce multiples of 256, why?

I was writing a test to see if I could reliably determine the integer value of an exit code with wait.

Questions

1. Why is the exit code multiplied by 256?
2. Is exit(), wait(), the OS, or something else doing the multiplication?

The code to reproduce the issue.

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

// implementation is correct but irrelevant to the question
int compareInt(const void* a, const void* b); 

int main(void) {
    pid_t pids[6];
    int i;
    for (i = 0; i < 6; i++) {
        pid_t pid = fork();
        pids[i] = pid;
        if (pid == 0) {
            exit(i);
        }
    }

    int codes[6];
    do {
        i--;
        wait(&codes[i]);
    } while (i > 0);

    const size_t num_elem = 6;
    qsort(codes, num_elem, sizeof(int), compareInt);

    for (i = 0; i < 5; i++) {
        printf("%d, ", codes[i]);
    }
    printf("%d\n", codes[5]);
    return 0;
}

Outputs: 0, 256, 512, 768, 1024, 1280

As it turns out I should be using wifexited(), wifstopped(), wifsignaled(), wexitstatus(), wtermsig(), or wstopsig() to determine the exit status.

In addition the same behaviour is reproducible in PHP (where I first encountered it)

$pids = [];
foreach (range(0, 5) as $i) {
    $pids[] = $pid = pcntl_fork();
    if ($pid === 0) {
        exit($i);
    }
}

$exit_codes = [];
do {
    pcntl_wait($exit_codes[]);
    array_pop($pids);
} while (count($pids) > 0);

sort($exit_codes);
echo implode(', ', $exit_codes) . "\n";

Outputs: 0, 256, 512, 768, 1024, 1280

If it makes a difference I am running Ubuntu 14.04 and man wait says I have WAIT(2)

like image 823
robbmj Avatar asked Nov 24 '14 23:11

robbmj


2 Answers

The reasons you're seeing those results is because the returned value from wait() is encoded and contains information such as how and why the process was stopped as well as the actual exit status; this is why convenience macros are supplied to inspect certain parts of the return value.

A definition of WEXITSTATUS(status) can be found in sys/wait.h and may look like this:

#define WEXITSTATUS(status) (((status) & 0xff00) >> 8)

Or this:

#define WEXITSTATUS(status) (((status) >> 8) & 0x000000ff)

So you're seeing the multiplication because:

exit(1) -> 0x0100 (256)
exit(2) -> 0x0200 (512)

In PHP you would use pcntl_wexitstatus() to accomplish the same thing; if the process was killed due to a signal there would be no exit status and you would need to use pcntl_wtermsig() to determine the signal that was used to kill it.

like image 196
Ja͢ck Avatar answered Sep 30 '22 17:09

Ja͢ck


The return value of wait() encodes several pieces of information. There are macros defined to pick apart the results and get just the nit you need. e.g. WEXITSTATUS(result_of_wait).

like image 31
John3136 Avatar answered Sep 30 '22 19:09

John3136