- 浏览: 101066 次
- 性别:
- 来自: 杭州
最新评论
-
johncan:
但是如果绑定错误,反而会有反效果,请问如何看是否绑定错误?是否 ...
erlang程序优化点的总结(持续更新) -
mahengyang:
如何使用mnesia:select/4分页查询%%对事务封装了 ...
mnesia监控项目 -
zjjxxl:
mysql代码高手,我们这里mysql5.6长时间运行内存过高 ...
innodb对B树游标的定位过程以及对“小于(等于)B树最小记录”的特殊处理 -
wqtn22:
是原创啊,霸爷mryufeng认证的,增加引用计数和减少引用计 ...
erlang NIF部分接口实现(三)持久资源 -
magicxiao:
enif_release_resource当引用计数为0的时候 ...
erlang NIF部分接口实现(三)持久资源
铁血的同学遇到这样一个问题,与之前4399同学遇到的问题类似,当初以为是erlang:ports/0的快照问题,仔细分析后发现另有玄机。
以下是问题描述:
>> 我使用的erlang版本是R15B03,进行socket处理的时候,如果在客户端到服务端有大概1000多个连接的时候(同时有较多的数据在发送),同时关闭所有客户端,
>> 这时在使用ports()查询出的端口中,有一些对其port_info/1返回的是undefined,
>> 并且这些undefined端口一样会占用端口数量,(出现这种情况的时候socket的控制进程确认已经都结束了)
>> 并且出现了这种undefined的端口,在使用init:stop()方式结束节点的时候就不能正常结束,只能使用halt(Status,
>> [{flush,false}])才行
>> 我尝试服务端再次调用或者不调用gen_tcp:close/1, erlang:port_close/1都不起作用
>>
>> 我socket的设置是这样的:
>> -define(TCP_OPTIONS, [binary, {packet, 0}, {active, false},
>> {reuseaddr, true}, {nodelay, false}, {delay_send, true},
>> {send_timeout, 5000}, {keepalive, false}, {exit_on_close, true}]).
>> socket处理这块使用的是rabbitmq的框架
初步分析认为场景是这样的:
若干tcp port上有大量数据发送时,关闭这些port,造成erlang:ports/0与erlang:port_info/1得到的port状态不一致,此时跟踪这两个函数如下:
erlang:ports/0:
BIF_RETTYPE ports_0(BIF_ALIST_0)
{
Eterm res = NIL;
Eterm* port_buf = erts_alloc(ERTS_ALC_T_TMP,
sizeof(Eterm)*erts_max_ports);
Eterm* pp = port_buf;
Eterm* dead_ports;
int alive, dead;
Uint32 next_ss;
int i;
erts_smp_mtx_lock(&ports_snapshot_mtx); /* One snapshot at a time */
erts_smp_atomic_set_nob(&erts_dead_ports_ptr,
(erts_aint_t) (port_buf + erts_max_ports));
next_ss = erts_smp_atomic32_inc_read_relb(&erts_ports_snapshot);
for (i = erts_max_ports-1; i >= 0; i--) {
Port* prt = &erts_port[i];
erts_smp_port_state_lock(prt);
if (!(prt->status & ERTS_PORT_SFLGS_DEAD)
&& prt->snapshot != next_ss) {
ASSERT(prt->snapshot == next_ss - 1);
*pp++ = prt->id;
prt->snapshot = next_ss; /* Consumed by this snapshot */
}
erts_smp_port_state_unlock(prt);
}
dead_ports = (Eterm*)erts_smp_atomic_xchg_nob(&erts_dead_ports_ptr,
(erts_aint_t) NULL);
erts_smp_mtx_unlock(&ports_snapshot_mtx);
ASSERT(pp <= dead_ports);
alive = pp - port_buf;
dead = port_buf + erts_max_ports - dead_ports;
ASSERT((alive+dead) <= erts_max_ports);
if (alive+dead > 0) {
erts_aint_t i;
Eterm *hp = HAlloc(BIF_P, (alive+dead)*2);
for (i = 0; i < alive; i++) {
res = CONS(hp, port_buf[i], res);
hp += 2;
}
for (i = 0; i < dead; i++) {
res = CONS(hp, dead_ports[i], res);
hp += 2;
}
}
erts_free(ERTS_ALC_T_TMP, port_buf);
BIF_RET(res);
}
erlang:ports/0这个函数将当前系统中处于非ERTS_PORT_SFLGS_DEAD状态的port快照组成一个列表返回,得到当前所有的活动port,这里有如下几点值得注意:
1.ERTS_PORT_SFLGS_DEAD状态由下列状态组成:
ERTS_PORT_SFLG_FREE|
ERTS_PORT_SFLG_FREE_SCHEDULED |
ERTS_PORT_SFLG_INITIALIZING;
2.若在调用erlang:ports/0进行统计的期间内,某个port退出,则这个port也将位于erlang:ports/0的返回列表内,但这些port在下次调用erlang:ports/0时将不会再次出现。
erlang:port_info/1
BIF_RETTYPE
port_info_1(BIF_ALIST_1)
{
Process* p = BIF_P;
Eterm pid = BIF_ARG_1;
static Eterm keys[] = {
am_name,
am_links,
am_id,
am_connected,
am_input,
am_output,
am_os_pid
};
Eterm items[ASIZE(keys)];
Eterm result = NIL;
Eterm reg_name;
Eterm* hp;
Uint need;
int i;
for (i = 0; i < ASIZE(keys); i++) {
Eterm item;
item = port_info(p, pid, keys[i]);
if (is_non_value(item)) {
return THE_NON_VALUE;
}
if (item == am_undefined) {
return am_undefined;
}
items[i] = item;
}
reg_name = port_info(p, pid, am_registered_name);
need = 2*ASIZE(keys);
if (is_tuple(reg_name)) {
need += 2;
}
hp = HAlloc(p, need);
for (i = ASIZE(keys) - 1; i >= 0; i--) {
result = CONS(hp, items[i], result);
hp += 2;
}
if (is_tuple(reg_name)) {
result = CONS(hp, reg_name, result);
}
return result;
}
static BIF_RETTYPE port_info(Process* p, Eterm portid, Eterm item)
{
BIF_RETTYPE ret;
Port *prt;
Eterm res;
Eterm* hp;
int count;
if (is_internal_port(portid))
prt = erts_id2port(portid, p, ERTS_PROC_LOCK_MAIN);
else if (is_atom(portid))
erts_whereis_name(p, ERTS_PROC_LOCK_MAIN,
portid, NULL, 0, 0, &prt);
else if (is_external_port(portid)
&& external_port_dist_entry(portid) == erts_this_dist_entry)
BIF_RET(am_undefined);
else
BIF_ERROR(p, BADARG);
if (!prt)
BIF_RET(am_undefined);
…
}
#define erts_id2port(ID, P, PL) \
erts_id2port_sflgs((ID), (P), (PL), ERTS_PORT_SFLGS_INVALID_LOOKUP)
ERTS_GLB_INLINE Port*
erts_id2port_sflgs(Eterm id, Process *c_p, ErtsProcLocks c_p_locks, Uint32 sflgs)
{
#ifdef ERTS_SMP
int no_proc_locks = !c_p || !c_p_locks;
#endif
Port *prt;
if (is_not_internal_port(id))
return NULL;
prt = &erts_port[internal_port_index(id)];
erts_smp_port_state_lock(prt);
if (ERTS_INVALID_PORT_OPT(prt, id, sflgs)) {
erts_smp_port_state_unlock(prt);
prt = NULL;
}
#ifdef ERTS_SMP
else {
erts_smp_atomic_inc_nob(&prt->refc);
erts_smp_port_state_unlock(prt);
if (no_proc_locks)
erts_smp_mtx_lock(prt->lock);
else if (erts_smp_mtx_trylock(prt->lock) == EBUSY) {
/* Unlock process locks, and acquire locks in lock order... */
erts_smp_proc_unlock(c_p, c_p_locks);
erts_smp_mtx_lock(prt->lock);
erts_smp_proc_lock(c_p, c_p_locks);
}
/* The id may not have changed... */
ERTS_SMP_LC_ASSERT(prt->id == id);
/* ... but status may have... */
if (prt->status & sflgs) {
erts_smp_port_unlock(prt); /* Also decrements refc... */
prt = NULL;
}
}
#endif
return prt;
}
erlang:port_info/1这个函数仅将当前系统中处于非ERTS_PORT_SFLGS_INVALID_LOOKUP状态的port的port_info返回,,这里有如下几点值得注意:
1.ERTS_PORT_SFLGS_DEAD状态由下列状态组成:
ERTS_PORT_SFLG_FREE |
ERTS_PORT_SFLG_FREE_SCHEDULED |
ERTS_PORT_SFLG_INITIALIZING |
ERTS_PORT_SFLG_INVALID |
ERTS_PORT_SFLG_CLOSING;
2.与erlang:ports/0的区别在于,除了前三个状态,erlang:port_info/1也不会将处于ERTS_PORT_SFLG_INVALID或ERTS_PORT_SFLG_CLOSING状态的port_info返回,其中ERTS_PORT_SFLG_INVALID不会真正赋予port,而ERTS_PORT_SFLG_CLOSING可以被赋予port。
ERTS_PORT_SFLG_CLOSING状态是当前这个场景的问题核心,若一个port处于ERTS_PORT_SFLG_CLOSING状态,而不处于ERTS_PORT_SFLGS_DEAD | ERTS_PORT_SFLG_FREE_SCHEDULED | ERTS_PORT_SFLG_INITIALIZING状态,则它将出现在erlang:ports/0的列表中,同时在erlang:port_info/1的结果中返回undefined。
对于port的ERTS_PORT_SFLG_CLOSING状态,由这个函数进行设置:
void erts_do_exit_port(Port *p, Eterm from, Eterm reason)
{
ErtsLink *lnk;
Eterm rreason;
ERTS_SMP_CHK_NO_PROC_LOCKS;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p));
rreason = (reason == am_kill) ? am_killed : reason;
if ((p->status & (ERTS_PORT_SFLGS_DEAD
| ERTS_PORT_SFLG_EXITING
| ERTS_PORT_SFLG_IMMORTAL))
|| ((reason == am_normal) &&
((from != p->connected) && (from != p->id)))) {
return;
}
if (IS_TRACED_FL(p, F_TRACE_PORTS)) {
trace_port(p, am_closed, reason);
}
erts_trace_check_exiting(p->id);
set_busy_port((ErlDrvPort)internal_port_index(p->id), 0);
if (p->reg != NULL)
(void) erts_unregister_name(NULL, 0, p, p->reg->name);
erts_port_status_bor_set(p, ERTS_PORT_SFLG_EXITING);
{
SweepContext sc = {p->id, rreason};
lnk = p->nlinks;
p->nlinks = NULL;
erts_sweep_links(lnk, &sweep_one_link, &sc);
}
DRV_MONITOR_LOCK_PDL(p);
{
ErtsMonitor *moni = p->monitors;
p->monitors = NULL;
erts_sweep_monitors(moni, &sweep_one_monitor, NULL);
}
DRV_MONITOR_UNLOCK_PDL(p);
if ((p->status & ERTS_PORT_SFLG_DISTRIBUTION) && p->dist_entry) {
erts_do_net_exits(p->dist_entry, rreason);
erts_deref_dist_entry(p->dist_entry);
p->dist_entry = NULL;
erts_port_status_band_set(p, ~ERTS_PORT_SFLG_DISTRIBUTION);
}
if ((reason != am_kill) && !is_port_ioq_empty(p)) {
erts_port_status_bandor_set(p,
~ERTS_PORT_SFLG_EXITING, /* must turn it off */
ERTS_PORT_SFLG_CLOSING);
flush_port(p);
}
else {
terminate_port(p);
}
}
这里进行一项检查,即对于一个非kill掉的port,若其发送队列不为空,则将port的状态加入ERTS_PORT_SFLG_CLOSING,同时flush该port,即调用port driver的flush回调。
调用erts_do_exit_port这个函数的位置各不相同,但其基本目的在于关闭一个port。
推测server设置了delay_send标志,在调用gen_tcp:close/1时,发送缓冲中仍然有若干数据未发送完,导致server不能正常关闭port,而使得port的中间状态ERTS_PORT_SFLG_CLOSING被暴露出来,结合当前问题,设计如下场景,意图重现该问题:
1.同时启动一个tcp client和一个tcp server;
2.client与server均为被动接收;
3.server缓冲发送,设置{delay_send,true}标志;
4.server一次发送大量数据,而client仅接收其中的一小部分,server的发送缓冲被占用而无法发出数据,然后调用gen_tcp:close关闭port;
5.实验场景与霸爷博客http://blog.yufeng.info/archives/1489
的这篇文章相似,实验代码也是在其上进行的改动,同时hack了一部分prim_inet的代码。
主要流程如下:
1.server启动
2.server listen创建监听套接字port,设置选项为{active, false}等;
3.server accept等待;
4.client启动
5.client connect,设置选项为{active,false}等;
6.server accept接受,创建已连接套接字port,设置选项为[{exit_on_close, true}, {delay_send,true}]等;
7.server 将port controlling_process给一个新进程,在这个新进程内进行控制;
8.server控制进程等待client请求;
9.client send请求”start”;
10.client等待server返回结果;
11.server recv client的”start”请求,send 1M数据到client;
12.client recv 1K数据
13.client send请求”bang”,通知server进行后续触发动作;
14.client 进行退出动作,不同的退出动作将产生不同的结果,包括如下退出动作:csr/csw/cc/cn/cr,这些动作稍后将进行解释;
15.server recv client的”bang”请求,通过inet:getstat(S, [send_pend])获取当前发送缓冲的长度;
16.server进行退出动作,不同的退出动作将产生不同的结果,包括如下退出动作:ssr/ssw/sc/scc/sn/su,这些动作稍后将进行解释;
17.server进行其它辅助工作,主要为接收最后一条tcp错误消息。
client的退出动作解释如下:
csr:shutdown read
csw:shutdown write
cc:close
cn:do nothing
cr:recv after 20 seconds
server的退出动作解释如下:
ssr:shutdown read
ssw:shutdown write
sc:close
scc:close with hacking gen_tcp:close/1,hacking的gen_tcp:close/1在延迟清空发送队列时,将无限发送直至成功,而非原来的5s内发送队列不变动则强行关闭port
sn:do nothing
su:unlink
这些不同动作的组合结果如下:
|
csr |
csw |
cc |
cn |
cr |
ssr |
server正常,port随控制进程退出而关闭; client因未调用close,port未释放,但可通过erlang:port_close释放 |
同csr+ ssr |
server正常,port随client提前关闭而关闭;client亦正常关闭port |
同csr+ ssr |
server正常,port随控制进程退出而关闭;client因未调用close,port未释放,但可通过erlang:port_close释放,client最后的recv无法收到数据 |
ssw |
同csr+ ssr |
同csr+ ssr |
同cc+ssr |
同cn+ssr |
同cr+ssr |
sc |
server的port的发送缓冲仍然有数据,但提前关闭该port,出现了erlang:ports/0与erlang:port_info/1不一致的情形,同时port无法再通过erlang:port_close/1释放;client因未调用close,port未释放,但可通过erlang:port_close释放;client调用erlang:port_close/1释放关闭port后,server的port也随之释放 |
同csr+sc |
server正常,port随client提前关闭而关闭;client亦正常关闭port |
同csr+sc |
同csr+sc,另外,client最后的recv无法收到数据 |
scc |
server的port的发送缓冲仍然有数据,但hacking后的erlang:port_info/1导致控制进程不断循环进行清空发送缓冲区的动作,从而不会出现erlang:ports/0与erlang:port_info/1不一致的情形;client因未调用close,port未释放,但可通过erlang:port_close释放;client调用erlang:port_close/1释放关闭port后,server的控制进程退出循环,port也随之释放 |
同csr+scc |
server正常,port随client提前关闭而关闭;client亦正常关闭port |
同csr+scc |
同csr+scc,另外,client最后的recv将接收到数据,同时令server的控制进程正常退出循环,port也随之释放 |
sn |
同csr+ ssr |
同csr+ ssr |
同cc+ssr |
同csr+ ssr |
同cr+ssr |
su |
server提前unlink了port,导致port仍然遗留,控制进程却已经退出,遗留port可以通过erlang:port_close释放;client因未调用close,port未释放,但可通过erlang:port_close释放 |
同csr+ su |
server提前unlink了port,导致port仍然遗留,控制进程却已经退出,遗留port可以通过erlang:port_close释放;client正常关闭port |
同csr+ su |
server提前unlink了port,导致port仍然遗留,控制进程却已经退出,遗留port可以通过erlang:port_close释放;client因未调用close,port未释放,但可通过erlang:port_close释放,client最后的recv无法收到数据 |
综上所述,server的port在delay_send选项控制下,若发送缓冲有数据却强行close,此时若client不close而仅仅是shutdown,则server的port将不能释放,并出现erlang:ports/0与erlang:port_info/1不一致的情形,解决的办法是server也进行shutdown,并令控制进程退出或client进行close。
分析gen_tcp:close/1的代码如下:
gen_tcp.erl
close(S) ->
inet:tcp_close(S).
inet.erl
tcp_close(S) when is_port(S) ->
%% if exit_on_close is set we must force a close even if remotely closed!!!
prim_inet:close(S),
receive {tcp_closed, S} -> ok after 0 -> ok end.
prim_inet.erl
close(S) when is_port(S) ->
unlink(S), %% avoid getting {'EXIT', S, Reason}
case subscribe(S, [subs_empty_out_q]) of
{ok, [{subs_empty_out_q,N}]} when N > 0 ->
close_pend_loop(S, N); %% wait for pending output to be sent
_ ->
catch erlang:port_close(S),
ok
end.
close_pend_loop(S, N) ->
receive
{empty_out_q,S} ->
catch erlang:port_close(S), ok
after ?INET_CLOSE_TIMEOUT ->
case getstat(S, [send_pend]) of
{ok, [{send_pend,N1}]} ->
if N1 =:= N -> catch erlang:port_close(S), ok;
true -> close_pend_loop(S, N1)
end;
_ ->
catch erlang:port_close(S), ok
end
end.
gen_tcp:close/1所作的工作包括:
1.unlink掉port与当前进程的关系,port成为无主port;
2.将调用进程作为port的一个订阅进程,通过subscribe函数订阅port的empty_out_q消息,这个消息仅在port的发送缓冲被清空时,由虚拟机投递给订阅进程(也即当前的调用进程);
3.循环等待empty_out_q消息的到达,或者5秒超时,若empty_out_q消息到达,则正常关闭port即可,若超时,则进入超时处理流程;
4.超时后,通过getstat(S, [send_pend])检查port的发送缓冲是否出现了变化,若未变化,则表明port的发送缓冲在过去的5秒内没能将任何数据发送出去,因此推测将来也不可能再将数据发送出去,因此强行关闭port,若出现了变化,则继续循环等待,直到port的发送缓冲清空;
5.强行关闭port将导致调用前述的erts_do_exit_port函数,在port的发送缓冲未清空的场景下,这个函数将设置port的状态为ERTS_PORT_SFLG_CLOSING,导致调用erlang:ports/0与erlang:port_info/1观察到了port的中间状态,得到不一致的结果;
6.这个问题不是资源泄露,而是发送缓冲这种设计机制导致的,port在close前必须将发送缓冲的数据全部推送到客户端,而客户端如果既不recv,也不close,而是shutdown半关闭或不作为,则导致server的port仍然被占用,此时连接仍然存在,只是难于被观察到,通过netstat可以看到client套接字处于FIN_WAIT2,而server套接字处于CLOSING,符合半关闭的状态。
gen_tcp:shutdown/2的经历和gen_tcp:close/1类似,霸爷已经在
http://blog.yufeng.info/archives/1489
一文中有了详细的分析,与gen_tcp:close/1不同的是gen_tcp:shutdown/2不会强行调用erlang:port_close/1关闭port,也就不会令中间状态暴露出来,当进程最终退出时,虚拟机帮助回收资源,进行真正的port关闭。
附件是测试代码。
- gen_tcp的close与delay_send交叉问题.zip (9.5 KB)
- 下载次数: 15
发表评论
-
erlang虚拟机topology不符导致启动后crash
2013-12-24 15:49 2010线上有一台t4的机器,这些机器的cpu topo是经过伪造 ... -
mnesia监控项目
2013-07-07 13:34 4511mnesia在运行时提供了大量的统计量,对这些统计量进行监 ... -
mnesia的index_read的性能问题
2013-06-23 16:12 3924我们在程序开发过程中,存在如下的一段代码: F = fu ... -
erlang程序优化点的总结(持续更新)
2013-03-03 15:40 11603转载请注明出处 注意,这里只是给出一个总结,具体性能 ... -
R15B01版本controlling_process一个port到self的问题
2013-01-07 21:43 2954在R15B01上,遇到一个gen_tcp/gen_udp: ... -
异步gen_server进行port访问时性能严重下降的原因和应对方法(五)套接字发送的应对
2012-06-30 23:35 3410复制前文的应对方法概 ... -
异步gen_server进行port访问时性能严重下降的原因和应对方法(四)文件写的应对
2012-06-30 23:08 1979对于“进程允许异步投递,但进程内部有调用port(receiv ... -
异步gen_server进行port访问时性能严重下降的原因和应对方法(三)典型场景
2012-06-30 22:28 2033上文解释了问题成因,即进程允许异步投递, 但进程内部有调用p ... -
异步gen_server进行port访问时性能严重下降的原因和应对方法(二)成因
2012-06-30 22:06 3016上文介绍了简单的问题场景,现在来分析下产生的原因。 cons ... -
异步gen_server进行port访问时性能严重下降的原因和应对方法(一)场景
2012-06-30 20:37 2310在进行项目开发时,有这么一个需求:一个生产者不断产生消息,并将 ...
相关推荐
erlang-gen_tcp手册,详细完整,网络tcp开发好东东
基于Erlang的gen_tcp库是其强大的网络编程接口,用于实现TCP协议的应用。在这个场景中,我们讨论的是一个使用Erlang和gen_tcp编写的聊天室应用。 **gen_tcp简介** gen_tcp是Erlang OTP(开放电信平台)提供的一种...
《gen_lex_hash_pc:MySQL交叉编译的关键工具详解》 在IT行业中,数据库管理系统是核心组件之一,而MySQL作为开源关系型数据库的代表,广泛应用于各类项目中。在特定环境下,如嵌入式设备或资源有限的PC平台,我们...
通用 TCP 服务器 通用 TCP 服务器( gen_tcp_server ) 是一种 Erlang 行为,提供快速简便的方法将 TCP 服务器功能添加到您的应用程序。 它被实现为管理 TCP 连接的主管,因为它是孩子。如何使用它? 运行make来构建。...
标题"Gen_Signature_Android2"指的是一个特定的工具,它用于生成Android应用的签名,这通常是在发布应用到Google Play或其他第三方市场之前所必需的步骤。这个工具可能是为简化开发者的工作流程而设计的,使得他们...
标题“Gen_Signature_Android2.zip”中的"Gen_Signature"指的是生成签名的过程,而"Android2"可能表示这是针对Android平台的第二个版本的工具或方法。这个压缩包文件包含一个名为"Gen_Signature_Android2.apk"的应用...
标题 "srio_response_gen_srio_gen2_0_srio_gen_srio_reponse_SRIO_gen2_SR" 提到的是一个与SRIO(Serial RapidIO)相关的响应生成模块,它可能是一个硬件描述语言(如Verilog或VHDL)设计的源代码文件。SRIO是一种...
1. **下载与安装**:获取Gen_Signature_Android.apk文件后,将其安装到Android设备或模拟器上。由于这是一款签名解析工具,可能需要绕过安全设置(如开启未知源安装)才能完成安装。 2. **运行工具**:启动应用后,...
《Android应用签名详解——以Gen_Signature_Android.apk为例》 在移动应用开发领域,尤其是Android系统中,应用的签名是确保软件安全性和完整性的关键环节。本篇文章将详细探讨Android应用签名的重要性、原理以及...
MKS Gen_L 主板是针对原有 Ramps1.4 开源主板存在的问题而设计的一款优化产品。它结合了 Arduino 2560 和 Ramps1.4 的功能,旨在提供更为稳定且易于使用的解决方案。这款主板适用于 3D 打印机制造商批量生产时作为...
gen_tags.vim, 用来轻松使用 ctags/gtags的vim和neovim的异步插件 gen_tags.vim 为方便用户使用 Vim/ NeoVim,简化了 ctags/ gtags的使用。它用于为你生成和维护多个平台支持的标签,在 Windows/Linux/macOS. 上测试...
本篇博客“gen_server tasting 之超简单名称服务(续)”主要探讨了如何使用gen_server来构建一个简单的名称服务系统,这是一个在分布式系统中常见的需求,用于存储和查找服务或资源的名称与地址的对应关系。...
这个“华硕Z87主板BIOS_updater_for_4th_Gen_Intel_Core_CPU.zip”压缩包就是用于更新该主板BIOS的工具,以确保与最新硬件和软件的兼容性,解决潜在问题,提高系统的稳定性和性能。 BIOS更新通常包含以下好处: 1. ...
gen_data_model库与“zookeeper”、“分布式”、“云原生”和“cloud native”这些标签紧密相关,意味着它可能被设计用于处理分布式系统中的数据模型。Zookeeper是一种分布式协调服务,常用于管理配置信息、命名服务...
在本文中,我们将深入探讨标题为“crc_gen_para_8_ip_crc-gen_verilogIP_crc_”的Verilog HDL实现的CRC校验IP核。这个IP核专门设计用于生成8位参数化的CRC码,适用于多种通讯协议的校验计算。 首先,让我们理解CRC...
pwm_gen,PWN波形发生器,开关波形
"dist_mem_gen_svp"是Xilinx提供的一个工具或IP核,全称为分布式内存生成器(Distributed Memory Generator)。这个工具主要用于创建FPGA内部的分布式RAM,它可以根据用户的需求定制大小、访问模式、数据宽度等参数...