Any benefit in using WEXITSTATUS macro in C over d

2019-01-11 11:36发布

问题:

I was doing an exercise for university where I had to return a value with exit, that value was actually a count of something. This could be above 255 (which exit() can't handle) but the teacher suggested to use test data where the count could never go above that value.

After all this, I needed to handle this count value, the exit status, I got this value in the main process by using waitpid(). To my surprise, if the child process returned 1, the "real" value in the main process was 256, 2 was 512 and so on...

I needed to print this value so I simply divided it by 256 and it was done. However, if I use the WEXITSTATUS() macro, I'll also get this value the way I want it...

I looked at C source code and this is what I found out:

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

I understand what's happening here, for instance, 512 in binary is 10 0000 0000, shifting 8 o the right will give 00 0000 0010, which is 2 in decimal. What I don't understand in this macro is that & operator and the fact that 0xff00 seems a random number (it probably isn't, where does it come from though?). What does this do exactly, why is there "& 0xff00" in the macro? Wouldn't it work with out it?

And the real question in this topic, is the same thing to call this macro in my code as dividing by 256?

回答1:

And the real question in this topic, is the same thing to call this macro in my code as dividing by 256?

It will probably always work in the cases where the child process terminates normally (i.e., by calling exit(), not by a segmentation fault, assertion failure, etc.).

The status stored by waitpid() encodes both the reason that the child process was terminated and the exit code. The reason is stored in the least-significant byte (obtained by status & 0xff), and the exit code is stored in the next byte (masked by status & 0xff00 and extracted by WEXITSTATUS()). When the process terminates normally, the reason is 0 and so WEXITSTATUS is just equivalent to shifting by 8 (or dividing by 256). However, if the process is killed by a signal (such as SIGSEGV), there is no exit code, and you have to use WTERMSIG to extract the signal number from the reason byte.



回答2:

If the status variable is a signed 16-bit integer (a 'short') on a machine where 'int' is a 32-bit quantity, and if the exit status is in the range 128..255, then WEXITSTATUS() still gives you a correct value where dividing by 256 or simply shifting right will give you an incorrect value.

This is because the short will be sign extended to 32-bits, and the masking undoes the the sign extension, leaving the correct (positive) value in the result.

If the machine used 16-bit integers, then the code in WEXITSTATUS() would probably do shift then mask to ensure similar behaviour:

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

It is because the implementation takes care of such details for you that you should use the WEXITSTATUS() macro.



回答3:

As far as I can tell from checking the Single Unix Spec, your system happens to store the exit status in the second-to-rightmost octet, but I don't believe the standard does. So, you should use the macros for at least a few reasons:

  • They're correct. Bit-shifting a negative number does different things on different platforms. Does it work the way you want on yours? I don't know.
  • They're simple. Its immediately clear what WEXITSTATUS does. Less so with other approaches. If you saw the hand-rolled version of WIFSIGNALED, would you recognize it? How much longer would it take than WIFSIGNALED.
  • They're portable. Since its how the spec says to do it, it'll work on every system (at least pretty much every Unix-like system).


回答4:

Here 0xff00 is a binary mask (link text). ANDing it with a value sets all bits to zero, except the second byte (counting from the right).

You should only use WEXITSTATUS on a process which is known to have exited normally. This information is given by the WIFEXITED macro.

And the real question in this topic, is the same thing to call this macro in my code as dividing by 256?

The macro makes the code more readable, and it's guaranted to work on any Posix-compliant implementation. As far as I know Posix doesn't specify the format of the status, so you can't expect your code to work everywhere.