Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c - Proper range of return status / value

Recently, when reading a book about linux programming, I got a message that:

The status argument given to _exit() defines the termination status of the process, which is available to the parent of this process when it calls wait(). Although defined as an int, only the bottom 8 bits of status are actually made available to the parent. And only 0 ~ 127 is recommanded to use, because 128 ~ 255 could be confusing in shell due to some reason. Due to that -1 will become 255 in 2's complement.

The above is about the exit status of a child process.

My question is:

  • Why the parent process only get the 8 bits of the child process's exit status?
  • What about return value of normal functions? Does it reasonable or befinit to use only 0 ~ 127 ? Because I do use -1 as return value to indicate error sometimes, should I correct that in future.

Update - status get by wait() / waitpid():

I read more chps in the book (TLPI), and found there are more trick in the return status & wait()/waitpid() that worth mention, I should have read more chps before ask the question. Anyhow, I have add an answer by myself to describe about it, in case it might help someone in future.

like image 391
user218867 Avatar asked Aug 31 '15 15:08

user218867


People also ask

What is the value of return in C?

A return statement ends the execution of a function, and returns control to the calling function. Execution resumes in the calling function at the point immediately following the call. A return statement can return a value to the calling function.

Where is the return value stored in C?

The return value is stored in registers - on x64 it goes in RAX. On x86, 64-bit structures like DateTime are returned in EAX:EDX.

What is function should return a value error in C?

If a function is defined as having a return type of void , it should not return a value. In C++, a function which is defined as having a return type of void , or is a constructor or destructor, must not return a value. If a function is defined as having a return type other than void , it should return a value.

What is the range of exit codes for an unsuccessful command execution?

Every command returns an exit status (sometimes referred to as a return status or exit code). A successful command returns a 0, while an unsuccessful one returns a non-zero value that usually can be interpreted as an error code.


Video Answer


2 Answers

Why the parent process only get the 8 bits of the child process's exit status?

Because POSIX says so. And POSIX says so because that's how original Unix worked, and many operating system derived from it and modeled after it continue to work.

What about return value of normal functions?

They are unrelated. Return whatever is reasonable. -1 is as good as any other value, and is in fact a standard way to indicate an error in a huge lot of standard C and POSIX APIs.

like image 70
n. 1.8e9-where's-my-share m. Avatar answered Oct 08 '22 05:10

n. 1.8e9-where's-my-share m.


The answer from @n.m. is good.

But later on, I read more chps in the book (TLPI), and found there are more trick in the return status & wait()/waitpid() that worth mention, and that might be another important or root reason why child process can't use full bits of int when exit.

Wait status

basicly:

  • child process should exit with value within range of 1 byte, which is set as part of the status parameter of wait() / waitpid(),
  • and only 2 LSB bytes of the status is used,

byte usage of status:

    event                   byte 1                  byte 0
    ============================================================
    * normal termination    exit status (0 ~ 255)   0
    * killed by signal      0                       termination signal (!=0)
    * stopped by signal     stop signal             0x7F
    * continued by signal               0xFFFF
    * 

dissect returned status:

header 'sys/wait.h',  defines a set of macros that help to dissect a wait status,

macros:
* WIFEXITED(status)
    return true if child process exit normally,
* 
* WIFSIGNALED(status)
    return true if child process killed by signal,
* WTERMSIG(status)
    return signal number that terminate the process,
* WCOREDUMP(status)
    returns ture if child process produced a core dump file,
    tip:
        this macro is not in SUSv3, might absent on some system,
        thus better check whether it exists first, via:
            #ifdef WCOREDUMP
                // ...
            #endif
* 
* WIFSTOPPED(status)
    return true if child process stopped by signal,
* WSTOPSIG(status)
    return signal number that stopp the process,
* 
* WIFCONTINUED(status)
    return true if child process resumed by signal SIGCONT,
    tip:
        this macro is part of SUSv3, but some old linux or some unix might didn't impl it,
        thus better check whether it exists first, via:
            #ifdef WIFCONTINUED
                // ...
            #endif
* 

Sample code

wait_status_test.c

// dissect status returned by wait()/waitpid()
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>

#define SLEEP_SEC 10 // sleep seconds of child process,

int wait_status_test() {
    pid_t cpid;

    // create child process,
    switch(cpid=fork()) {
        case -1: // failed
            printf("error while fork()\n");
            exit(errno);
        case 0: // success, child process goes here
            sleep(SLEEP_SEC);
            printf("child [%d], going to exit\n",(int)getpid());
            _exit(EXIT_SUCCESS);
            break;
        default: // success, parent process goes here
            printf("parent [%d], child created [%d]\n", (int)getpid(), (int)cpid);
            break;
    }

    // wait child to terminate
    int status;
    int wait_flag = WUNTRACED | WCONTINUED;
    while(1) {
        if((cpid = waitpid(-1, &status, wait_flag)) == -1) {
            if(errno == ECHILD) {
                printf("no more child\n");
                exit(EXIT_SUCCESS);
            } else {
                printf("error while wait()\n");
                exit(-1);
            }
        }
        // disset status
        printf("parent [%d], child [%d] ", (int)getpid(), (int)cpid);
        if(WIFEXITED(status)) { // exit normal
            printf("exit normally with [%d]\n", status);
        } else if(WIFSIGNALED(status)) { // killed by signal
            char *dumpinfo = "unknow";
            #ifdef WCOREDUMP
                dumpinfo = WCOREDUMP(status)?"true":"false";
            #endif
            printf("killed by signal [%d], has dump [%s]\n", WTERMSIG(status), dumpinfo);
        } else if(WIFSTOPPED(status)) { // stopped by signal
            printf("stopped by signal [%d]\n", WSTOPSIG(status));
        #ifdef WIFCONTINUED
        } else if(WIFCONTINUED(status)) { // continued by signal
            printf("continued by signal SIGCONT\n", WSTOPSIG(status));
        #endif
        } else { // this should never happen
            printf("unknow event\n");
        }
    }

    return 0;
}

int main(int argc, char *argv[]) {
    wait_status_test();
    return 0;
}

Compile:

gcc -Wall wait_status_test.c

Execute:

  • ./a.out and wait it to terminate normally, child process id is printed after fork(),
  • ./a.out, then kill -9 <child_process_id> before it finish sleep,
  • ./a.out, then kill -STOP <child_process_id> before it finish sleep, then kill -CONT <child_process_id> to resume it,
like image 2
user218867 Avatar answered Oct 08 '22 04:10

user218867