简体   繁体   中英

What is the size of /proc/%u/fd/%u

How much size do I need to allocate for /proc/%u/fd/%u ?

In strace code they allocated char path[sizeof("/proc/%u/fd/%u") + 2 * sizeof(int)*3];

I didn't understand the calc, how did they calc this size?

That may seem a rather bizarre way to do it, but it works (see below). They're assuming that you'll need no more than 2 + 3n characters for both the process ID and file descriptor, where n is the number of bytes in an integer ( 2 because the %u will be replaced, 3n because of the sizeof component).

It does actually work, as per the following chars/int values (for 8-bit char ), formula results, and the maximum unsigned int that will come from it:

chars/int  sizeof(int) * 3 + 2       max value
---------  -------------------  --------------------
        1                    5                   255 (3/5 needed)
        2                    8                 65535 (5/8 needed)
        4                   14            4294967296 (10/14)
        8                   26  18446744073709551616 (20/26)
       16                   50           3.3 * 10^38 (39/50)

You can see that the storage allocated keeps up with the storage required, to the point where you could just as easily use:

char path[sizeof("/proc/%u/fd/%u") + 2 * sizeof(int) * 3 - 2];
//                                              add this ^^^

And it will also work for char sizes beyond eight bits as well since doubling the bits in a value at most doubles the number of decimal digits (the table shows this with the needed size: 3, 5, 10, 20, 39, ... ). That, combined with the fact that the int doubles in size as well, means there will always be enough space.


You may think that, for safety and portability, you could actually get the maximum PID and process file descriptor respectively from /proc/sys/kernel/pid_max and /proc/<pid>/limits (programatically) and use that to dynamically allocate enough space to hold the information:

pax:~> cat /proc/sys/kernel/pid_max
32768
pax:~> cat /proc/self/limits | awk '$1$2$3=="Maxopenfiles"{print $5}'
4096

Hence the first %u would need 5 characters and the second four characters.


Or, you could just allocate 20 characters for each on the likely case that you won't see 66-bit PIDs or FDs anytime in the near future:-) You could still check against the procfs files, if only to exit gracefully if they were too large.


But, to be honest, both of those are overkill, since you may already have a thing that's meant to provide the limit on file paths, PATH_MAX (see limits.h ), for Linux. It's probably okay to use that and wear the minimal wastage. And, if you don't have that, you could either get the value from maximum length with a call to pathconf() . Or choose a sensible default (say, 60 characters), then just use snprintf to detect if the actual path was longer than your buffer, and exit gracefully.

If you really wanted to use the optimal method for minimal storage (ie, you think a PATH_MAX of 4K is too much wastage) then, by all means, use the strace method, but with two subtracted as per my earlier comment (a) .


(a) If you're going to worry about 4K here and there, you may as well do it fully:-)

The best answer is, replace this calculation for MAX_PATH (include <limits.h> ) that makes your code to more cleaner and portable.

The easy answer is to just use PATH_MAX because in this case it's going to be fine.

The more complicated version is what strace does.

That strace code does seem to have the right idea. It seems like a fairly safe assumption that for any given byte (0..255) you'll need up to 3 decimal places to represent it.

That calculation presumes that a 4-byte int can always be represented by 3x the decimal places, or no more than 12 digits.

In actuality you'd need just 10, so there's a safety margin here, and room for future-proofing if int ever gets bigger for some reason.

Update

Here's a quick demonstration of the strlen vs. sizeof issue:

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

int main() {
  // Character array
  char tmpl[] = "/proc/%u/fd/%u";

  printf("sizeof(char[])=%lu\n", sizeof(tmpl));

  // Character pointer
  char* ptr = "/proc/%u/fd/%u";

  printf("sizeof(char*)=%lu\n", sizeof(ptr));

  // String literal without variable
  printf("sizeof(\"...\")=%lu\n", sizeof("/proc/%u/fd/%u"));

  return 0;
}

For me on a 64-bit system this produces the results:

sizeof(char[])=15
sizeof(char*)=8
sizeof("...")=15

Where sizeof("...any string...") is always going to be the same thing, the size of a char pointer, or in other words 8 on a 64-bit system.

The tmpl[] definition defines an array which while largely interchangeable with pointers due to array to pointer decay , does have size information available to the compiler in the scope in which it is defined.

If you're using Linux,asprintf(3) will allocate enough memory to hold the final string for you so you don't have to mess with variable-length arrays or working out the needed length ahead of time:

char *path = NULL;
if (asprintf(&path, "/proc/%u/fd/%u", pid, fd) < 0) {
    // Handle error
}
// Use path
free(path); // Don't forget to free it when done.


You can also use snprintf(3) with a 0 length buffer to get the needed length, and then call it again with an appropriately sized buffer:

int len = snprintf(NULL, 0, "/proc/%u/fd/%u", pid, fd);
if (len < 0) {
    // Handle error
}
char buf[len + 1]; // Need to add one for the trailing nul
if (snprintf(buf, len + 1, "/proc/%u/fd/%u", pid, fd) != len) {
    // Handle error
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM