正如其他人所解释的那样,/proc
和/sys
是伪文件系统,由内核提供的数据组成,直到读取时实际上并不存在 - 内核生成该数据。由于大小不同,并且在打开文件进行读取之前确实未知,因此根本不向用户空间提供。
然而,这并不是“不幸的”。同样的情况经常发生,例如字符设备(在/dev
下),管道,FIFO(命名管道)和套接字。
我们可以轻松编写一个帮助函数,使用动态内存管理完全读取伪文件。例如:
#define _POSIX_C_SOURCE 200809L
#define _ATFILE_SOURCE
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
int at_dir(const int dirfd, const char *dirpath)
{
if (dirfd == -1 || !dirpath || !*dirpath) {
errno = EINVAL;
return -1;
}
return openat(dirfd, dirpath, O_DIRECTORY | O_PATH | O_CLOEXEC);
}
size_t read_pseudofile_at(const int dirfd, const char *path, char **dataptr, size_t *sizeptr)
{
char *data;
size_t size, have = 0;
ssize_t n;
int desc;
if (!path || !*path || !dataptr || !sizeptr) {
errno = EINVAL;
return 0;
}
size = *sizeptr;
if (!size)
*dataptr = NULL;
data = *dataptr;
desc = openat(dirfd, path, O_RDONLY | O_CLOEXEC | O_NOCTTY);
if (desc == -1) {
return 0;
}
while (1) {
if (have >= size) {
size = (have | 4095) + 4097 - 32;
data = realloc(data, size);
if (!data) {
close(desc);
errno = ENOMEM;
return 0;
}
*dataptr = data;
*sizeptr = size;
}
n = read(desc, data + have, size - have);
if (n > 0) {
have += n;
} else
if (n == 0) {
break;
} else
if (n == -1) {
const int saved_errno = errno;
close(desc);
errno = saved_errno;
return 0;
} else {
close(desc);
errno = EIO;
return 0;
}
}
if (close(desc) == -1) {
return 0;
}
if (have + 32 > size)
memset(data + have, 0, 32);
else
memset(data + have, 0, size - have);
errno = 0;
return have;
}
int main(void)
{
char *data = NULL;
size_t size = 0;
size_t len;
int selfdir;
selfdir = at_dir(AT_FDCWD, "/proc/self/");
if (selfdir == -1) {
fprintf(stderr, "/proc/self/ is not available: %s.\n", strerror(errno));
exit(EXIT_FAILURE);
}
len = read_pseudofile_at(selfdir, "status", &data, &size);
if (errno) {
fprintf(stderr, "/proc/self/status: %s.\n", strerror(errno));
exit(EXIT_FAILURE);
}
printf("/proc/self/status: %zu bytes\n%s\n", len, data);
len = read_pseudofile_at(selfdir, "maps", &data, &size);
if (errno) {
fprintf(stderr, "/proc/self/maps: %s.\n", strerror(errno));
exit(EXIT_FAILURE);
}
printf("/proc/self/maps: %zu bytes\n%s\n", len, data);
close(selfdir);
free(data); data = NULL; size = 0;
return EXIT_SUCCESS;
}
上面的示例程序打开了一个目录描述符(“atfile handle”)到/proc/self
。(这样你就不需要连接字符串来构建路径。)
然后它读取了/proc/self/status的内容。如果成功,它会显示其大小(以字节为单位)和其内容。
接下来,它重用先前的缓冲区读取/proc/self/maps的内容。如果成功,它也会显示其大小和内容。
最后,由于不再需要,关闭目录描述符,并释放动态分配的缓冲区。
请注意,执行free(NULL)
是完全安全的,且在read_pseudofile_at()
调用之间丢弃动态缓冲区(free(data); data=NULL; size=0;
)也是安全的。
由于伪文件通常很小,read_pseudofile_at()
使用线性动态缓冲区增长策略。如果没有先前的缓冲区,它将从8160个字节开始,并在之后每次增加4096个字节,直到足够大。随意用您喜欢的增长策略替换它,这只是一个示例,但在实践中效果非常好,不会浪费太多内存。