我需要为/proc/%u/fd/%u
分配多少大小?
在strace代码中,他们分配了char path[sizeof("/proc/%u/fd/%u") + 2 * sizeof(int)*3];
我不理解计算方式,他们是如何计算出这个大小的?
这可能看起来是一种非常奇怪的方法,但它能够正常工作(见下文)。他们假设你需要不超过2+3n
个字符来表示进程ID和文件描述符,其中n
是一个整数的字节数(2
代表%u
将被替换,3n
代表sizeof
部分)。
实际上,这确实可以工作,以下是针对8位char
的chars/int
值、公式结果和最大的无符号整数:
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)
您可以看到所分配的存储空间与所需存储空间保持一致,以至于您可以轻松地使用:
char path[sizeof("/proc/%u/fd/%u") + 2 * sizeof(int) * 3 - 2];
// add this ^^^
此外,即使对于超过八位的char
大小也可以使用此方法,因为在值中将位数翻倍最多会将十进制数字的数量翻倍(表格显示所需大小为:3, 5, 10, 20, 39, ...
)。再加上int
也会翻倍,这意味着总是有足够的空间。
您可能会认为,为了安全和可移植性,您实际上可以从/proc/sys/kernel/pid_max
和/proc/<pid>/limits
(以编程方式)获取最大PID和进程文件描述符,并使用它来动态分配足够的空间来容纳信息:
pax:~> cat /proc/sys/kernel/pid_max
32768
pax:~> cat /proc/self/limits | awk '$1$2$3=="Maxopenfiles"{print $5}'
4096
%u
需要5个字符,第二个需要4个字符。
procfs
文件,如果它们太大就优雅地退出。
PATH_MAX
(参见limits.h
)。使用它并将浪费降到最低可能是可以接受的。如果你没有这个值,你可以通过调用pathconf()
获取最大长度的值。或者选择一个明智的默认值(比如60个字符),然后只需使用snprintf
来检测实际路径是否超过了缓冲区的长度,并优雅地退出。PATH_MAX
是太多的浪费),那么请尽管使用strace
方法,但按照我的早期评论减去两个字符。
pid_max
设为 131072
,因此它们变得更大了。假设某件事情有点冒险,你会在挖掘那些 /proc
文件以寻找答案时消耗超过 ~4KB 的内存。 - tadman/proc/sys/kernel/pid_max
和 /proc/self/limits
..只是为了获取 proc/pid/
的缓冲区大小会很奇怪;-) 使用 MAX_PATH
或者甚至只是将其硬编码为类似于256的东西,这是一个更好的选择。 - P.PMAX_PATH
(4kb),如果我们可以安全地使用更少的空间呢? - MicrosoctCprogMAX_PATH
(包括<limits.h>
),这样可以使你的代码更加清晰和可移植。PATH_MAX
。而且PATH_MAX
不必定义:“在特定的实现中,如果相应的值等于或大于所述最小值,但该值可能因适用的文件而异,则以下列表中一个符号常量的定义将从<limits.h>
头文件中省略。对于特定路径名支持的实际值应由pathconf()
函数提供。” - Andrew Henle如果你使用Linux,asprintf(3)
会为你分配足够的内存来容纳最终字符串,因此你不必去处理可变长度数组或提前计算所需的长度:
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.
snprintf(3)
以获取所需的长度,然后再使用适当大小的缓冲区重新调用它: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
}
snprintf()
计算所需确切长度的方法。 - ShawnPATH_MAX
,因为在这种情况下它应该没问题。int
始终可以用3倍的十进制位表示,或者不超过12位数字。int
因某种原因变得更大,则有未来的余地。strlen
与sizeof
问题的快速演示:#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;
}
对于我而言,64位系统会产生以下结果:
sizeof(char[])=15
sizeof(char*)=8
sizeof("...")=15
sizeof("...任何字符串...")
始终是相同的,即char
指针的大小,换句话说,在64位系统上为8。
tmpl[]
定义了一个数组,虽然由于数组到指针的衰减而在很大程度上可以与指针互换,但在其定义所在的范围内编译器确实可以获得其大小信息。
sizeof("/proc/%u/fd/%u")
不等于 strlen("/proc/%u/fd/%u")
,因此代码是错误的。 - brunoconst char [N]
,所以在这种情况下大小是正确的。使用strlen
会使数组成为一个在运行时计算大小而不是编译时计算大小的VLA。 - phuclvsizeof
在字符串字面量上可以正常工作。试一下就知道了。在你的例子中,你使用它在一个 char*
上,这不是 strace 代码所做的。 - interjayconst
一直是C语言的一部分。它只是没有被广泛使用。 - tadmanconst
关键字本身。为了更加清晰明确:在C语言中,字符串字面值的类型不是const char [N]
,而是char [N]
。 - P.P
MAX_PATH
。除非你的空间很紧,否则为什么要冒险呢?我也希望你正在使用snprintf
而不是sprintf
。 - tadman/proc/sys/kernel/pid_max
之间的某个值,而文件描述符将是远小于/proc/sys/fs/file-max
的某个值,因此您可以通过对这两个值都取floor(log10(x)+1)
来计算最小安全缓冲区大小。话虽如此,使用MAX_PATH
肯定是更简单的选择。 - Felix Gchar proc_fd_path[sizeof("/proc//fd/") + (((sizeof(pid_t) + sizeof(int)) + 1)*10*CHAR_BIT - 2)/33 + 1];
;我会让读者自行解决为什么这会始终为任何/proc/%u/fd/%u
可能抛出来的问题保留足够的内存。提示 log2(10) ≈ 3.3 - datenwolf