网站建设人员职责分布昭通网站seo优化
- 作者: 五速梦信息网
- 时间: 2026年03月21日 07:44
当前位置: 首页 > news >正文
网站建设人员职责分布,昭通网站seo优化,至高建设集团 网站,怎样建设好门户网站欢迎直接到博客 linux alsa-lib snd_pcm_open函数源码分析#xff08;三) 系列文章其他部分: linux alsa-lib snd_pcm_open函数源码分析#xff08;一) linux alsa-lib snd_pcm_open函数源码分析#xff08;二) linux alsa-lib snd_pcm_open函数源码分析#xff08;四…欢迎直接到博客 linux alsa-lib snd_pcm_open函数源码分析三) 系列文章其他部分: linux alsa-lib snd_pcm_open函数源码分析一) linux alsa-lib snd_pcm_open函数源码分析二) linux alsa-lib snd_pcm_open函数源码分析四) linux alsa-lib snd_pcm_open函数源码分析五) linux alsa-lib snd_pcm_open函数源码分析六) 解析配置的最后一个超复杂子函数
- snd_config_hooks
核心函数之一配置hooks功能其中使用hooks功能加载并解析了配置文件中引用的其他配置文件。 所谓的其他配置文件通常为/etc/asound.conf及/.asoundrc也就是用户经常自定义修改的配置文件。
static int snd_config_hooks_call(snd_config_t *root, snd_config_t *config, snd_config_t *private_data)
{void *h NULL;snd_config_t *c, *func_conf NULL;char *buf NULL;const char *lib NULL, *func_name NULL;const char *str;int (*func)(snd_config_t *root, snd_config_t *config, snd_config_t **dst, snd_config_t *private_data) NULL;int err;//在配置树中通过id寻找某个节点//实际通常这里func后会跟一个load,比如alsa.conf中的func load//目的是加载其他配置//见下文详细分析err snd_config_search(config, func, c);if (err 0) {SNDERR(Field func is missing);return err;}//功能及实现都很简单//返回string类型节点的string值//本质上就是直接把节点的值返回一下//见下文分析err snd_config_get_string(c, str);if (err 0) {SNDERR(Invalid type for field func);return err;}assert(str);//配置树中查找hook_func节点//详细分析见下文err snd_config_search_definition(root, hook_func, str, func_conf);if (err 0) {snd_config_iterator_t i, next;if (snd_config_get_type(func_conf) ! SND_CONFIG_TYPE_COMPOUND) {SNDERR(Invalid type for func %s definition, str);err -EINVAL;goto _err;}snd_config_for_each(i, next, func_conf) {snd_config_t *n snd_config_iterator_entry(i);const char *id n-id;if (strcmp(id, comment) 0)continue;if (strcmp(id, lib) 0) {err snd_config_get_string(n, lib);if (err 0) {SNDERR(Invalid type for %s, id);goto _err;}continue;}if (strcmp(id, func) 0) {err snd_config_get_string(n, func_name);if (err 0) {SNDERR(Invalid type for %s, id);goto _err;}continue;}SNDERR(Unknown field %s, id);}}if (!func_name) {int len 16 strlen(str) 1;buf malloc(len);if (! buf) {err -ENOMEM;goto _err;}//这里即可拼接出函数的名字snprintf(buf, len, snd_confighook%s, str);buf[len-1] \0;func_name buf;}//对dlopen的包装打开库如果指定的库不存在则打开默认的库h snd_dlopen(lib, RTLD_NOW);//对dlsym的包装从动态库中解析函数符号即通过字符串查找到函数func h ? snd_dlsym(h, func_name, SND_DLSYM_VERSION(SND_CONFIG_DLSYM_VERSION_HOOK)) : NULL;err 0;if (!h) {SNDERR(Cannot open shared library %s, lib);err -ENOENT;} else if (!func) {SNDERR(symbol %s is not defined inside %s, func_name, lib);snd_dlclose(h);err -ENXIO;}_err:if (func_conf)snd_config_delete(func_conf);if (err 0) {snd_config_t *nroot;//这里执行了根据字符串查找出来的函数err func(root, config, nroot, private_data);if (err 0)SNDERR(function %s returned error: %s, func_name, snd_strerror(err));snd_dlclose(h);if (err 0 nroot)err snd_config_substitute(root, nroot);}free(buf);if (err 0)return err;return 0;
}1.1 snd_config_search
在配置树中通过id查找一个子节点。id可以是一个或多个中间以圆点(.)分隔。 如果是多个id的情况则每个id需要依次指定前一级的复合节点。 比如在下面的配置树中假设config是复合节点的句柄, 每个节点后的注释为找到这个节点需要用到的key config {a 42 # ab { # bc cee # b.cd { # b.de 2.71828 # b.d.e}}}函数原型如下:
int snd_config_search(snd_config_t *config, const char *key, snd_config_t **result)
{SND_CONFIG_SEARCH(config, key, result, );
}实际主要是使用了SND_CONFIG_SEARCH宏这类宏有多个后面会对所有类型的宏都做详细的分析。
1.1.1 SND_CONFIG_SEARCH
此宏是相对来说最简单的宏实现的目的比较单一即通过Key查找节点。 其中主要调用了_snd_config_search函数此函数在上一篇中已经分析过。
#define SND_CONFIG_SEARCH(config, key, result, extra_code)
{ \snd_config_t *n; \int err; \const char *p; \assert(config key); \while (1) { \//如果不是复合节点直接报错。//这是因为传入的config本身即为父节点如果不是复合节点则本身就不应该在一个单身节点下面查找子节点if (config-type ! SND_CONFIG_TYPE_COMPOUND) \return -ENOENT; \//这里执行extra_code,这也是为什么宏中可以添加代码的原因。{ extra_code ; } \//此函数返回key中第一次出现字符.的位置如果每找到则返回nullp strchr(key, .); \if (p) { \//如果找到,则返回(.)的位置,注意此处的p-key//由于返回的是.的位置即.的地址key为字符串最开头的地址//这样p-key即为字符串开始到.的字符的个数//于是搜索key字符串的前p-key个字符则变成了搜索key字符串第一个点前的字符串//比如搜索a.b.c的话此时相当于搜索aerr _snd_config_search(config, key, p - key, n); \if (err 0) \return err; \//注意这里如果搜索到了返回n则把n赋值为config,下个循环则从n开始搜索config n; \//p1的目的是跳过.)这里的1其实就是这个(.)//这样一来在下个循环的时候相当于从刚刚搜索到的节点开始搜索(.)后面的内容key p 1; } else \//如果没有(.)则传下来的key就是要搜索的内容return _snd_config_search(config, key, -1, result); }
}1.2 snd_config_get_string 非常简单返回string类型节点的string值。 int snd_config_get_string(const snd_config_t *config, const char **ptr) {assert(config ptr);if (config-type ! SND_CONFIG_TYPE_STRING)return -EINVAL;*ptr config-u.string;return 0; }1.3 snd_config_search_definition 在配置树中查找节点函数允许传入的参数为别名(alias),同时如果传入的参数为别名函数会还把别名展开。 如果传入的名字中包含冒号(,则冒号(:)后则为snd_config_expand展开所用的参数。 int snd_config_search_definition(snd_config_t *config,const char *base, const char *name,snd_config_t result) {snd_config_t *conf;char key;//查找://如果返回参数作为char,则实际是找到的:的地址const char *args strchr(name, :);int err;if (args) {//如果找到了:,则自增1目的是跳过:这个字符args;//args - name则等于:前的字符长度1,多出的1个字符刚好作为\0key alloca(args - name);//注意这里需要把多出来的\0字符的位置减掉//实际刚好为:前的内容memcpy(key, name, args - name - 1);//最后的位置为\0key[args - name - 1] \0;} else {//找不到则直接就是keykey (char *) name;}/ if key contains dot (.), the implicit base is ignored* and the key starts from root given by the config parameter*/snd_config_lock();//如果key中有(.),则传入的base会被忽略//否则如果没有找到(.)说明没有指明base,则采用传入的base//函数查找子结点如果传入的是别名(alias)则可以展开别名//详细见下文分析err snd_config_search_alias_hooks(config, strchr(key, .) ? NULL : base, key, conf);if (err 0) {snd_config_unlock();return err;}//展开节点并执行对应的函数//见下文分析err snd_config_expand(conf, config, args, NULL, result);snd_config_unlock();return err; }1.3.1 snd_config_search_alias_hooks 在配置树下使用别名及hooks查找节点。主要用来实现snd_config_search_definition的功能。 对别两个函数snd_config_search_definition的第三个参数name可以包含’:‘ 如果有’:‘则’:‘后的内容为snd_config_expand的参数,而snd_config_search_alias_hooks的第三个参数更纯粹 就是key,无法包含’:用于snd_config_expand。 int snd_config_search_alias_hooks(snd_config_t *config,const char *base, const char *key,snd_config_t **result) {SND_CONFIG_SEARCH_ALIAS(config, base, key, result,snd_config_searcha_hooks,snd_config_searchva_hooks); }这里出现了SND_CONFIG_SEARCH_ALIAS宏与此类似的宏由多个这些宏分别实现类似的功能但是彼此之间有差异。 这些宏分析起来及其复杂因为大部分都涉及到递归处理以及相互嵌套。 这些函数中最基础的有两个在此做介绍其他类似的函数的函数只做功能说明. 1.3.2 snd_config_searcha 通过key在配置树中查找节点展开别名。注意与1.1 snd_config_search的区别。 config {a {b bb}}root {bb {c cc}cc cccccc {d {x icks}}}在上面的配置树中使用snd_config_searcha(root, config, a.b.c.d, result);则最终返回d节点。 int snd_config_searcha(snd_config_t *root, snd_config_t *config, const char *key, snd_config_t **result) {SND_CONFIG_SEARCHA(root, config, key, result, snd_config_searcha, ); }1.3.2.1 SND_CONFIG_SEARCHA 此宏主要用来实现snd_config_searcha,使用key查找一个配置节点同时在root下查找是否有别名并展开。 注意传入的fcn为调用者本身意味着此处会有递归处理。 #define SND_CONFIG_SEARCHA(root, config, key, result, fcn, extra_code)
{ \snd_config_t *n; \int err; \const char *p; \assert(config key); \while (1) { \//如果不是复合类型通常意味着搜索结束或者错误if (config-type ! SND_CONFIG_TYPE_COMPOUND) { \//获取string类型的字符串值if (snd_config_get_string(config, p) 0) \return -ENOENT; \//递归处理err fcn(root, root, p, config); \if (err 0) \return err; } { extra_code ; } \//此处是查看key中是否有(.)p strchr(key, .); \if (p) { \err _snd_config_search(config, key, p - key, n); \if (err 0) \return err; \//把找到的n赋值给config,相当于从root逐步往下查找config n; \key p 1; } else \return _snd_config_search(config, key, -1, result); }
}此类函数的功能所使用的宏以及主要作用总结在下表: 函数名功能说明使用到的宏snd_config_search在配置树中根据key查找节点SND_CONFIG_SEARCHsnd_config_searcha在配置树中根据key查找节点展开别名。别名从root下查找SND_CONFIG_SEARCHAsnd_config_searchv在配置树中根据key查找节点key可以是一系列的多个keySND_CONFIG_SEARCHVsnd_config_searchva在配置树中根据key查找节点展开别名key可以是连续多个keySND_CONFIG_SEARCHVAsnd_config_search_alias在配置树中根据key查找节点展开别名.与snd_config_searcha类似但是只能在config下查找。如果config下找不到id,则函数会尝试寻找base.idSND_CONFIG_SEARCH_ALIASsnd_config_search_hooks在配置树中根据key查找节点,并且展开hooks。与snd_config_search类似但是搜索的任何包含hooks的节点都会被各自的hooks函数修改SND_CONFIG_SEARCHsnd_config_searcha_hooks在配置树中根据key查找节点,并且展开alias与hooksSND_CONFIG_SEARCHAsnd_config_searchva_hooks在配置树中根据key查找节点,并且展开alias与hooks。与snd_config_searcha_hooks类似但是key可以是一系列的keySND_CONFIG_SEARCHVAsnd_config_search_alias_hooks在配置树中根据key查找节点,并且展开alias与hooks。与snd_config_search_alias相似并且展开hooks与snd_config_search_hooks相似SND_CONFIG_SEARCH_ALIAS 1.3.2 snd_config_expand 使用参数及函数展开一个配置节点。如果传入的这个节点中有参数(通过一个id为args的子节点定义), 则这个函数会用各自的参数值,或默认的参数值或者空来取代任何以$开头的string节点。 而且任何函数都会被评估参考snd_config_evaluate),结果的副本将会在result中返回。 这里评估(evaluated)的意思比较模糊从代码分析上看应该是所有的函数都被执行了。 int snd_config_expand(snd_config_t *config, snd_config_t *root, const char *args,snd_config_t *private_data, snd_config_t **result) {int err;snd_config_t *defs, *subs NULL, *res;//寻找参数//前面已分析过err snd_config_search(config, args, defs);if (err 0) {if (args ! NULL) {SNDERR(Unknown parameters %s, args);return -EINVAL;}//创建config的副本到res,注意是深层拷贝//也就是说如果config是复合节点//它的子节点也会被拷贝//详细见下文分析err snd_config_copy(res, config);if (err 0)return err;} else {//如果找到参数则直接创建一个top节点//前文已分析err snd_config_top(subs);if (err 0)return err;//把defs里面的default“节点添加到空的subs里面//详细见下文分析err load_defaults(subs, defs);if (err 0) {SNDERR(Load defaults error: %s, snd_strerror(err));goto _end;}//解析参数args//太太太复杂err parse_args(subs, args, defs);if (err 0) {SNDERR(Parse arguments error: %s, snd_strerror(err));goto _end;}//在运行时评估一个配置节点err snd_config_evaluate(subs, root, private_data, NULL);if (err 0) {SNDERR(Args evaluate error: %s, snd_strerror(err));goto _end;} }err snd_config_walk(config, root, res, _snd_config_expand, subs);if (err 0) {SNDERR(Expand error (walk): %s, snd_strerror(err));goto _end;}}err snd_config_evaluate(res, root, private_data, NULL);if (err 0) {SNDERR(Evaluate error: %s, snd_strerror(err));snd_config_delete(res);goto _end;}*result res;err 1;_end:if (subs)snd_config_delete(subs);return err; }1.3.2.1 snd_config_evaluate 在运行时评估一个函数此函数会评估配置树中的任何一个函数(func) 并用各自函数的结果替换这些节点。这里的评估应该时计算的意思。 int snd_config_evaluate(snd_config_t *config, snd_config_t *root,snd_config_t private_data, snd_config_t **result) {/ FIXME: Only in place evaluation is currently implemented */assert(result NULL);return snd_config_walk(config, root, result, _snd_config_evaluate, private_data); }1.3.2.2 snd_config_walk 这里面传入的回调函数为_snd_config_evaluate,函数本身又会有递归 大概目的就是一步一步查找func找到并执行并且由于创建了新的配置树 会把执行函数后的节点信息替换掉原来的节点。 static int snd_config_walk(snd_config_t *src,snd_config_t *root,snd_config_t **dst,snd_config_walk_callback_t callback,snd_config_t *private_data) {int err;snd_config_iterator_t i, next;switch (snd_config_get_type(src)) {case SND_CONFIG_TYPE_COMPOUND:err callback(src, root, dst, SND_CONFIG_WALK_PASS_PRE, private_data);if (err 0)return err;snd_config_for_each(i, next, src) {snd_config_t *s snd_config_iterator_entry(i);snd_config_t *d NULL;err snd_config_walk(s, root, (dst *dst) ? d : NULL,callback, private_data);if (err 0)goto _error;if (err d) {err snd_config_add(*dst, d);if (err 0)goto _error;}}err callback(src, root, dst, SND_CONFIG_WALK_PASS_POST, private_data);if (err 0) {_error:if (dst *dst)snd_config_delete(*dst);}break;default:err callback(src, root, dst, SND_CONFIG_WALK_PASS_LEAF, private_data);break;}return err; }1.3.2.2 _snd_config_evaluate 具体执行函数太复杂,即有循环又有递归注意里面的snd_dlopen 及snd_dlsym,会从动态库中根据func符号找到函数并且执行。 所以func其实是执行了evaluate可以理解为计算的意思。 static int _snd_config_evaluate(snd_config_t *src,snd_config_t *root,snd_config_t **dst ATTRIBUTE_UNUSED,snd_config_walk_pass_t pass,snd_config_t *private_data) {int err;if (pass SND_CONFIG_WALK_PASS_PRE) {char *buf NULL;const char *lib NULL, *func_name NULL;const char *str;int (*func)(snd_config_t **dst, snd_config_t *root,snd_config_t *src, snd_config_t *private_data) NULL;void *h NULL;snd_config_t *c, *func_conf NULL;err snd_config_search(src, func, c);if (err 0)return 1;err snd_config_get_string(c, str);if (err 0) {SNDERR(Invalid type for func);return err;}assert(str);err snd_config_search_definition(root, func, str, func_conf);if (err 0) {snd_config_iterator_t i, next;if (snd_config_get_type(func_conf) ! SND_CONFIG_TYPE_COMPOUND) {SNDERR(Invalid type for func %s definition, str);err -EINVAL;goto _err;}snd_config_for_each(i, next, func_conf) {snd_config_t *n snd_config_iterator_entry(i);const char *id n-id;if (strcmp(id, comment) 0)continue;if (strcmp(id, lib) 0) {err snd_config_get_string(n, lib);if (err 0) {SNDERR(Invalid type for %s, id);goto _err;}continue;}if (strcmp(id, func) 0) {err snd_config_get_string(n, func_name);if (err 0) {SNDERR(Invalid type for %s, id);goto _err;}continue;}SNDERR(Unknown field %s, id);}}if (!func_name) {int len 9 strlen(str) 1;buf malloc(len);if (! buf) {err -ENOMEM;goto _err;}snprintf(buf, len, sndfunc%s, str);buf[len-1] \0;func_name buf;}h snd_dlopen(lib, RTLD_NOW);if (h)func snd_dlsym(h, func_name, SND_DLSYM_VERSION(SND_CONFIG_DLSYM_VERSION_EVALUATE));err 0;if (!h) {SNDERR(Cannot open shared library %s, lib);err -ENOENT;goto _errbuf;} else if (!func) {SNDERR(symbol %s is not defined inside %s, func_name, lib);snd_dlclose(h);err -ENXIO;goto _errbuf;}_err:if (func_conf)snd_config_delete(func_conf);if (err 0) {snd_config_t eval;err func(eval, root, src, private_data);if (err 0)SNDERR(function %s returned error: %s, func_name, snd_strerror(err));snd_dlclose(h);if (err 0 eval) {/ substitute merges compound members // we dont want merging at all */err snd_config_delete_compound_members(src);if (err 0)//替换节点err snd_config_substitute(src, eval);}}_errbuf:free(buf);if (err 0)return err;return 0;}return 1; }至此snd_config_expand的功能为展开节点从节点中搜索函数逐个执行并最终用执行结果替换掉原来的节点。 snd_config_search_definition则查找某个定义查找到后使用snd_config_expand去展开执行。 根据读取alsa的默认配置文件alsa.conf,通常会构建一个func load函数再用构造出的函数去load配置文件。 里面又是一大堆递归。总之alsa配置的目的就是通过在配置文件中修改配置即可控制运行时的函数执行。 为了实现这个目标alsa丧心病狂的实现了众多嵌套递归为了实现代码复用采用了众多的宏整体让alsa变得及其复杂。 比如alsa.conf中的配置: hooks [{func loadfiles [/etc/alsa/conf.d/etc/asound.conf/.asoundrc]errors false} ]通过这个hooks实际上构造了snd_config_hooks_load函数,运行时会解析符号并从动态库中找到此函数 用来加载接下来的三个配额文件/etc/alsa/conf.d及/etc/asound.conf,~/.asoundrc。 至此snd_config_update_r的大致功能已经比较清晰了从配置文件中读取配置文件解析为配置树 执行所有的hooks其中可能又会包含读取配置文件解析为配置树。执行所有的hooks函数重新更新配置树。 返回最后的配置树。
相关文章
-
网站建设人员要与客户谈什么网站架构分析工具
网站建设人员要与客户谈什么网站架构分析工具
- 技术栈
- 2026年03月21日
-
网站建设人员信息宁波网站建设接单
网站建设人员信息宁波网站建设接单
- 技术栈
- 2026年03月21日
-
网站建设人员的分工西南能矿建设工程公司网站
网站建设人员的分工西南能矿建设工程公司网站
- 技术栈
- 2026年03月21日
-
网站建设认准猪八戒网wordpress菜单添加首页
网站建设认准猪八戒网wordpress菜单添加首页
- 技术栈
- 2026年03月21日
-
网站建设任职要求wordpress360
网站建设任职要求wordpress360
- 技术栈
- 2026年03月21日
-
网站建设如何搞活动石材网站模板
网站建设如何搞活动石材网站模板
- 技术栈
- 2026年03月21日



