gpt4 book ai didi

c - getpwuid_r 和 getgrgid_r 很慢,如何缓存它们?

转载 作者:太空宇宙 更新时间:2023-11-03 23:22:22 25 4
gpt4 key购买 nike

我在学习 C 的同时正在编写 find 的克隆。在实现 -ls 选项时,我偶然发现了一个问题,即 getpwuid_rgetgrgid_r 调用真的很慢,同样适用于 getpwuidgetgrgid。我需要它们根据 stat.h 提供的 ID 显示用户/组名。

例如,列出整个文件系统会慢 3 倍:

# measurements were made 3 times and the fastest run was recorded

# with getgrgid_r

time ./myfind / -ls > list.txt

real 0m4.618s
user 0m1.848s
sys 0m2.744s


# getgrgid_r replaced with 'return "user";'

time ./myfind / -ls > list.txt

real 0m1.437s
user 0m0.572s
sys 0m0.832s

我想知道GNU find 是如何保持这么好的速度的。我看过 sources , 但如果没有特殊类型、宏等,它们就不太容易理解和应用:

time find / -ls > list.txt

real 0m1.544s
user 0m0.884s
sys 0m0.648s

我考虑过在数据结构中缓存 uid - usernamegid - groupname 对。这是不是一个好主意?您将如何实现?

你可以找到我的完整代码here .

更新:

solution正是我要找的:

time ./myfind / -ls > list.txt

real 0m1.480s
user 0m0.696s
sys 0m0.736s

这里是一个基于getgrgid的版本(如果你不需要线程安全):

char *do_get_group(struct stat attr) {
struct group *grp;

static unsigned int cache_gid = UINT_MAX;
static char *cache_gr_name = NULL;

/* skip getgrgid if we have the record in cache */
if (cache_gid == attr.st_gid) {
return cache_gr_name;
}

/* clear the cache */
cache_gid = UINT_MAX;

grp = getgrgid(attr.st_gid);

if (!grp) {
/*
* the group is not found or getgrgid failed,
* return the gid as a string then;
* an unsigned int needs 10 chars
*/
char group[11];
if (snprintf(group, 11, "%u", attr.st_gid) < 0) {
fprintf(stderr, "%s: snprintf(): %s\n", program, strerror(errno));
return "";
}
return group;
}

cache_gid = grp->gr_gid;
cache_gr_name = grp->gr_name;

return grp->gr_name;
}

getpwuid:

char *do_get_user(struct stat attr) {
struct passwd *pwd;

static unsigned int cache_uid = UINT_MAX;
static char *cache_pw_name = NULL;

/* skip getpwuid if we have the record in cache */
if (cache_uid == attr.st_uid) {
return cache_pw_name;
}

/* clear the cache */
cache_uid = UINT_MAX;

pwd = getpwuid(attr.st_uid);

if (!pwd) {
/*
* the user is not found or getpwuid failed,
* return the uid as a string then;
* an unsigned int needs 10 chars
*/
char user[11];
if (snprintf(user, 11, "%u", attr.st_uid) < 0) {
fprintf(stderr, "%s: snprintf(): %s\n", program, strerror(errno));
return "";
}
return user;
}

cache_uid = pwd->pw_uid;
cache_pw_name = pwd->pw_name;

return pwd->pw_name;
}

更新 2:

long 更改为 unsigned int

更新 3:

添加了缓存清除。这是绝对必要的,因为pwd->pw_name可能指向一个静态区域。 getpwuid 可以在失败时或仅在程序中的其他地方执行时覆盖其内容。

同时删除了 strdup。由于 getgrgidgetpwuid 的输出 should not be freed ,我们的包装函数不需要 free

最佳答案

时间确实表明对这些功能的强烈怀疑。

查看您的函数 do_get_group,存在一些问题:

  • 你使用 sysconf(_SC_GETPW_R_SIZE_MAX); 每次调用 do_get_groupdo_get_user,一定要缓存它,它不会改变在你的程序的生命周期中,但你不会有太多收获。

  • 您使用 attr.st_uid 而不是 attr.st_gid,这可能会导致许多文件的查找失败,可能会破坏缓存机制(如果有) .先修复这个,这是一个错误!

  • 您返回的值不应由调用者传递给 free(),例如 grp->gr_name""。你应该总是分配你返回的字符串。 do_get_user() 中可能存在同样的问题。

这是用一次性缓存替换 do_get_group 的方法。看看这是否提高了性能:

/*
* @brief returns the groupname or gid, if group not present on the system
*
* @param attr the entry attributes from lstat
*
* @returns the groupname if getgrgid() worked, otherwise gid, as a string
*/
char *do_get_group(struct stat attr) {
char *group;
struct group grp;
struct group *result;

static size_t length = 0;
static char *buffer = NULL;
static gid_t cache_gid = -1;
static char *cache_gr_name = NULL;

if (!length) {
/* only allocate the buffer once */
long sysconf_length = sysconf(_SC_GETPW_R_SIZE_MAX);

if (sysconf_length == -1) {
sysconf_length = 16384;
}

length = (size_t)sysconf_length;
buffer = calloc(length, 1);
}
if (!buffer) {
fprintf(stderr, "%s: malloc(): %s\n", program, strerror(errno));
return strdup("");
}

/* check the cache */
if (cache_gid == attr.st_gid) {
return strdup(cache_gr_name);
}

/* empty the cache */
cache_gid = -1;
free(cache_gr_name);
cache_gr_name = NULL;

if (getgrgid_r(attr.st_gid, &grp, buffer, length, &result) != 0) {
fprintf(stderr, "%s: getpwuid_r(): %s\n", program, strerror(errno));
return strdup("");
}

if (result) {
group = grp.gr_name;
} else {
group = buffer;
if (snprintf(group, length, "%ld", (long)attr.st_gid) < 0) {
fprintf(stderr, "%s: snprintf(): %s\n", program, strerror(errno));
return strdup("");
}
}

/* load the cache */
cache_gid = attr.st_gid;
cache_gr_name = strdup(group);

return strdup(group);
}

关于c - getpwuid_r 和 getgrgid_r 很慢,如何缓存它们?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35821628/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com