Mono为什么取消兼容文件名大小写功能
前言
在传统Asp.Net WebForm/MVC项目从Windows系统迁移到Linux系统,大多数会使用Jexus来代替Windows上的IIS,在Jexus早期的shell运行脚本中,是有一项配置的.开启之后,可以忽略文件名大小写.重要的事情要标红,Windows系统文件名不区分大小写,Linux/类Unix文件名都是区分大小写的.
#export MONO_IOMAP="all" #iomap为all时,忽略文件名大小写
在从Windows系统迁移时,这个功能还是很好的,因为不需要考虑文件名大小写的问题了,后面为什么会去取消这个功能呢?说到底还是因为性能.在Linux系统,如果开启忽略文件名大小写配置后.
比如在加载index.aspx时:
- 最好的情况,index.aspx文件名就是这个,找到这个文件就行了
- 假设这个文件名是indeX.aspx,就需要多次尝试是否可以打开这个index.aspx
这种多次尝试打开文件,是很影响性能的,所以在2021年的时候,Mono对这个功能的源码进行了移除.我们可以看看这一块的源码:
#include "config.h"
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <errno.h>
#include <mono/metadata/profiler-private.h>
#include <mono/utils/mono-compiler.h>
#include <mono/utils/mono-io-portability.h>
#ifndef DISABLE_PORTABILITY
#include <dirent.h>
int mono_io_portability_helpers = PORTABILITY_UNKNOWN;
static gchar *mono_portability_find_file_internal(const gchar *pathname,
gboolean last_exists);
void mono_portability_helpers_init(void) {
gchar *env;
if (mono_io_portability_helpers != PORTABILITY_UNKNOWN)
return;
mono_io_portability_helpers = PORTABILITY_NONE;
env = g_getenv("MONO_IOMAP"); //从环境变量中获取MONO_IOMAP
if (env != NULL) {
/* parse the environment setting and set up some vars
* here
*/
gchar **options = g_strsplit(env, ":", 0);
int i;
if (options == NULL) {
/* This shouldn't happen */
return;
}
for (i = 0; options[i] != NULL; i++) {
#ifdef DEBUG
g_message("%s: Setting option [%s]", __func__, options[i]);
#endif
if (!strncasecmp(options[i], "drive", 5)) {
mono_io_portability_helpers |= PORTABILITY_DRIVE;
} else if (!strncasecmp(options[i], "case", 4)) {
mono_io_portability_helpers |= PORTABILITY_CASE;
} else if (!strncasecmp(options[i], "all", 3)) { //如果是all
mono_io_portability_helpers |= (PORTABILITY_DRIVE | PORTABILITY_CASE);
}
}
g_free(env);
}
}
/* Returns newly allocated string, or NULL on failure */
static gchar *find_in_dir(DIR *current, const gchar *name) {
struct dirent *entry;
#ifdef DEBUG
g_message("%s: looking for [%s]\n", __func__, name);
#endif
while ((entry = readdir(current)) != NULL) {
#ifdef DEBUGX
g_message("%s: found [%s]\n", __func__, entry->d_name);
#endif
if (!g_ascii_strcasecmp(name, entry->d_name)) {
char *ret;
#ifdef DEBUG
g_message("%s: matched [%s] to [%s]\n", __func__, entry->d_name, name);
#endif
ret = g_strdup(entry->d_name);
closedir(current);
return ret;
}
}
#ifdef DEBUG
g_message("%s: returning NULL\n", __func__);
#endif
closedir(current);
return (NULL);
}
//mono调用这个方法,查找文件
gchar *mono_portability_find_file(const gchar *pathname, gboolean last_exists) {
gchar *ret;
if (!pathname || !pathname[0])
return NULL;
ret = mono_portability_find_file_internal(pathname, last_exists);
return ret;
}
//真正干活的
/* Returns newly-allocated string or NULL on failure */
static gchar *mono_portability_find_file_internal(const gchar *pathname,
gboolean last_exists) {
gchar *new_pathname, **components, **new_components;
int num_components = 0, component = 0;
DIR *scanning = NULL;
size_t len;
if (IS_PORTABILITY_NONE) {
return (NULL);
}
new_pathname = g_strdup(pathname);
#ifdef DEBUG
g_message("%s: Finding [%s] last_exists: %s\n", __func__, pathname,
last_exists ? "TRUE" : "FALSE");
#endif
//******先用access判断文件是否存在,如果存在直接返回
if (last_exists && access(new_pathname, F_OK) == 0) {
#ifdef DEBUG
g_message("%s: Found it without doing anything\n", __func__);
#endif
return (new_pathname);
}
/* First turn '\' into '/' and strip any drive letters */
g_strdelimit(new_pathname, '\\', '/');
#ifdef DEBUG
g_message("%s: Fixed slashes, now have [%s]\n", __func__, new_pathname);
#endif
if (IS_PORTABILITY_DRIVE && g_ascii_isalpha(new_pathname[0]) &&
(new_pathname[1] == ':')) {
int len = strlen(new_pathname);
g_memmove(new_pathname, new_pathname + 2, len - 2);
new_pathname[len - 2] = '\0';
#ifdef DEBUG
g_message("%s: Stripped drive letter, now looking for [%s]\n", __func__,
new_pathname);
#endif
}
len = strlen(new_pathname);
if (len > 1 && new_pathname[len - 1] == '/') {
new_pathname[len - 1] = 0;
#ifdef DEBUG
g_message("%s: requested name had a trailing /, rewritten to '%s'\n",
__func__, new_pathname);
#endif
}
//继续判断文件是否存在
if (last_exists && access(new_pathname, F_OK) == 0) {
#ifdef DEBUG
g_message("%s: Found it\n", __func__);
#endif
return (new_pathname);
}
/* OK, have to work harder. Take each path component in turn
* and do a case-insensitive directory scan for it
*/
if (!(IS_PORTABILITY_CASE)) {
g_free(new_pathname);
return (NULL);
}
components = g_strsplit(new_pathname, "/", 0);
if (components == NULL) {
/* This shouldn't happen */
g_free(new_pathname);
return (NULL);
}
while (components[num_components] != NULL) {
num_components++;
}
g_free(new_pathname);
if (num_components == 0) {
return NULL;
}
new_components = (gchar **)g_new0(gchar **, num_components + 1);
if (num_components > 1) {
if (strcmp(components[0], "") == 0) {
/* first component blank, so start at / */
scanning = opendir("/");
if (scanning == NULL) {
#ifdef DEBUG
g_message("%s: opendir 1 error: %s", __func__, g_strerror(errno));
#endif
g_strfreev(new_components);
g_strfreev(components);
return (NULL);
}
new_components[component++] = g_strdup("");
} else {
DIR *current;
gchar *entry;
current = opendir(".");
if (current == NULL) {
#ifdef DEBUG
g_message("%s: opendir 2 error: %s", __func__, g_strerror(errno));
#endif
g_strfreev(new_components);
g_strfreev(components);
return (NULL);
}
entry = find_in_dir(current, components[0]);
if (entry == NULL) {
g_strfreev(new_components);
g_strfreev(components);
return (NULL);
}
scanning = opendir(entry);
if (scanning == NULL) {
#ifdef DEBUG
g_message("%s: opendir 3 error: %s", __func__, g_strerror(errno));
#endif
g_free(entry);
g_strfreev(new_components);
g_strfreev(components);
return (NULL);
}
new_components[component++] = entry;
}
} else {
if (last_exists) {
if (strcmp(components[0], "") == 0) {
/* First and only component blank */
new_components[component++] = g_strdup("");
} else {
DIR *current;
gchar *entry;
current = opendir(".");
if (current == NULL) {
#ifdef DEBUG
g_message("%s: opendir 4 error: %s", __func__, g_strerror(errno));
#endif
g_strfreev(new_components);
g_strfreev(components);
return (NULL);
}
entry = find_in_dir(current, components[0]);
if (entry == NULL) {
g_strfreev(new_components);
g_strfreev(components);
return (NULL);
}
new_components[component++] = entry;
}
} else {
new_components[component++] = g_strdup(components[0]);
}
}
#ifdef DEBUG
g_message("%s: Got first entry: [%s]\n", __func__, new_components[0]);
#endif
g_assert(component == 1);
// 继续尝试查找
for (; component < num_components; component++) {
gchar *entry;
gchar *path_so_far;
if (!last_exists && component == num_components - 1) {
entry = g_strdup(components[component]);
closedir(scanning);
} else {
entry = find_in_dir(scanning, components[component]);
if (entry == NULL) {
g_strfreev(new_components);
g_strfreev(components);
return (NULL);
}
}
new_components[component] = entry;
if (component < num_components - 1) {
path_so_far = g_strjoinv("/", new_components);
scanning = opendir(path_so_far);
g_free(path_so_far);
if (scanning == NULL) {
g_strfreev(new_components);
g_strfreev(components);
return (NULL);
}
}
}
g_strfreev(components);
new_pathname = g_strjoinv("/", new_components);
#ifdef DEBUG
g_message("%s: pathname [%s] became [%s]\n", __func__, pathname,
new_pathname);
#endif
g_strfreev(new_components);
//最后在看文件是否存在
if ((last_exists && access(new_pathname, F_OK) == 0) || (!last_exists)) {
return (new_pathname);
}
g_free(new_pathname);
return (NULL);
}
#else /* DISABLE_PORTABILITY */
MONO_EMPTY_SOURCE_FILE(mono_io_portability);
#endif /* DISABLE_PORTABILITY */
移除MONO_IOMAP功能的代码,都在这里,有兴趣的可以去学习一下:Mono移除MONO_IOMAP代码
秋风
2023-02-18