- 浏览: 985804 次
- 性别:
- 来自: 广州
最新评论
-
qingchuwudi:
有用,非常感谢!
erlang进程的优先级 -
zfjdiamond:
你好 这条命令 在那里输入??
你们有yum 我有LuaRocks -
simsunny22:
这个是在linux下运行的吧,在window下怎么运行escr ...
escript的高级特性 -
mozhenghua:
http://www.erlang.org/doc/apps/ ...
mnesia 分布协调的几个细节 -
fxltsbl:
A new record of 108000 HTTP req ...
Haproxy 1.4-dev2: barrier of 100k HTTP req/s crossed
erlang通过port来spawn外部程序 重定向外部程序的stdin, stdout到一对pipe行通信的,利用poll来检测外部程序的读写事件。但是如果外部程序退出的话,erts如何知道并且加以处理的呢?
erts运行的时候会初始化smp_sig_notify,开启一个信号处理线程,在这个线程里面做具体的信号处理。
static void
init_smp_sig_notify(void)
{
erts_smp_thr_opts_t thr_opts = ERTS_SMP_THR_OPTS_DEFAULT_INITER;
thr_opts.detached = 1;
if (pipe(sig_notify_fds) < 0) {
erl_exit(ERTS_ABORT_EXIT,
"Failed to create signal-dispatcher pipe: %s (%d)\n",
erl_errno_id(errno),
errno);
}
/* Start signal handler thread */
erts_smp_thr_create(&sig_dispatcher_tid,
signal_dispatcher_thread_func,
NULL,
&thr_opts);
}
static void *
signal_dispatcher_thread_func(void *unused)
{
int initialized = 0;
#if !CHLDWTHR
int notify_check_children = 0;
#endif
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_set_thread_name("signal_dispatcher");
#endif
erts_thread_init_fp_exception();
while (1) {
char buf[32];
int res, i;
/* Block on read() waiting for a signal notification to arrive... */
res = read(sig_notify_fds[0], (void *) &buf[0], 32);
if (res < 0) {
if (errno == EINTR)
continue;
erl_exit(ERTS_ABORT_EXIT,
"signal-dispatcher thread got unexpected error: %s (%d)\n",
erl_errno_id(errno),
errno);
}
for (i = 0; i < res; i++) {
/*
* NOTE 1: The signal dispatcher thread should not do work
* that takes a substantial amount of time (except
* perhaps in test and debug builds). It needs to
* be responsive, i.e, it should only dispatch work
* to other threads.
*
* NOTE 2: The signal dispatcher thread is not a blockable
* thread (i.e., it hasn't called
* erts_register_blockable_thread()). This is
* intentional. We want to be able to interrupt
* writing of a crash dump by hitting C-c twice.
* Since it isn't a blockable thread it is important
* that it doesn't change the state of any data that
* a blocking thread expects to have exclusive access
* to (unless the signal dispatcher itself explicitly
* is blocking all blockable threads).
*/
switch (buf[i]) {
case 0: /* Emulator initialized */
initialized = 1;
#if !CHLDWTHR
if (!notify_check_children)
#endif
break;
#if !CHLDWTHR
case 'C': /* SIGCHLD */
if (initialized)
erts_smp_notify_check_children_needed();
else
notify_check_children = 1;
break;
#endif
case 'I': /* SIGINT */
break_requested();
break;
case 'Q': /* SIGQUIT */
quit_requested();
break;
case '1': /* SIGUSR1 */
sigusr1_exit();
break;
#ifdef QUANTIFY
case '2': /* SIGUSR2 */
quantify_save_data(); /* Might take a substantial amount of
time, but this is a test/debug
build */
break;
#endif
default:
erl_exit(ERTS_ABORT_EXIT,
"signal-dispatcher thread received unknown "
"signal notification: '%c'\n",
buf[i]);
}
}
ERTS_SMP_LC_ASSERT(!ERTS_LC_IS_BLOCKING);
}
return NULL;
}
void
erts_sys_main_thread(void)
{
/* Become signal receiver thread... */
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_set_thread_name("signal_receiver");
#endif
smp_sig_notify(0); /* Notify initialized */
while (1) {
/* Wait for a signal to arrive... */
#ifdef DEBUG
int res =
#else
(void)
#endif
select(0, NULL, NULL, NULL, NULL);
ASSERT(res < 0);
ASSERT(errno == EINTR);
}
}
因为外部的程序是fork exec来执行的,所以退出的时候erts进程就会受到SIGCHLD信号。
static int spawn_init()
{
...
sys_sigset(SIGCHLD, onchld); /* Reap children */
...
}
onchld就会被调用
static RETSIGTYPE onchld(int signum)
{
#if CHLDWTHR
ASSERT(0); /* We should *never* catch a SIGCHLD signal */
#elif defined(ERTS_SMP)
smp_sig_notify('C');
#else
children_died = 1;
ERTS_CHK_IO_INTR(1); /* Make sure we don't sleep in poll */
#endif
}
static void
smp_sig_notify(char c)
{
int res;
do {
/* write() is async-signal safe (according to posix) */
res = write(sig_notify_fds[1], &c, 1);
} while (res < 0 && errno == EINTR);
if (res != 1) {
char msg[] =
"smp_sig_notify(): Failed to notify signal-dispatcher thread "
"about received signal";
(void) write(2, msg, sizeof(msg));
abort();
}
}
于是erts_smp_notify_check_children_needed()被调用。
void
erts_smp_notify_check_children_needed(void)
{
ErtsSchedulerData *esdp;
erts_smp_sched_lock();
for (esdp = schedulers; esdp; esdp = esdp->next)
esdp->check_children = 1;
if (block_multi_scheduling) {
/* Also blocked schedulers need to check children */
erts_smp_mtx_lock(&msched_blk_mtx);
for (esdp = schedulers; esdp; esdp = esdp->next)
esdp->blocked_check_children = 1;
erts_smp_cnd_broadcast(&msched_blk_cnd);
erts_smp_mtx_unlock(&msched_blk_mtx);
}
wake_all_schedulers();
erts_smp_sched_unlock();
}
这个函数设置调度器的check_children的标志 并且唤醒所有的调度器。
调度器的入口process_main我们来看下如何处理的:
Process *schedule(Process *p, int calls)
{
...
if (esdp->check_children) {
esdp->check_children = 0;
erts_smp_sched_unlock();
erts_check_children();
erts_smp_sched_lock();
}
...
}
调用erts_check_children。
void
erts_check_children(void)
{
(void) check_children();
}
static int check_children(void)
{
int res = 0;
int pid;
int status;
#ifndef ERTS_SMP
if (children_died)
#endif
{
sys_sigblock(SIGCHLD);
CHLD_STAT_LOCK;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
note_child_death(pid, status);
#ifndef ERTS_SMP
children_died = 0;
#endif
CHLD_STAT_UNLOCK;
sys_sigrelease(SIGCHLD);
res = 1;
}
return res;
}
static void note_child_death(int pid, int status)
{
ErtsSysReportExit **repp = &report_exit_list;
ErtsSysReportExit *rep = report_exit_list;
while (rep) {
if (pid == rep->pid) {
*repp = rep->next;
ERTS_REPORT_EXIT_STATUS(rep, status);
break;
}
repp = &rep->next;
rep = rep->next;
}
}
static ERTS_INLINE void
report_exit_status(ErtsSysReportExit *rep, int status)
{
Port *pp;
#ifdef ERTS_SMP
CHLD_STAT_UNLOCK;
#endif
pp = erts_id2port_sflgs(rep->port,
NULL,
0,
ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP);
#ifdef ERTS_SMP
CHLD_STAT_LOCK;
#endif
if (pp) {
if (rep->ifd >= 0) {
driver_data[rep->ifd].alive = 0;
driver_data[rep->ifd].status = status;
(void) driver_select((ErlDrvPort) internal_port_index(pp->id),
rep->ifd,
DO_READ,
1);
}
if (rep->ofd >= 0) {
driver_data[rep->ofd].alive = 0;
driver_data[rep->ofd].status = status;
(void) driver_select((ErlDrvPort) internal_port_index(pp->id),
rep->ofd,
DO_WRITE,
1); }
erts_port_release(pp);
}
erts_free(ERTS_ALC_T_PRT_REP_EXIT, rep);
}
移除对该port的监视 销毁port.
static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd)
{
...
res = read(ready_fd, read_buf, ERTS_SYS_READ_BUF_SZ);
if (res < 0) {
if ((errno != EINTR) && (errno != ERRNO_BLOCK))
port_inp_failure(port_num, ready_fd, res);
}
else if (res == 0)
port_inp_failure(port_num, ready_fd, res);
else
driver_output(port_num, (char*) read_buf, res);
erts_free(ERTS_ALC_T_SYS_READ_BUF, (void *) read_buf);
}....
}
static int port_inp_failure(int port_num, int ready_fd, int res)
/* Result: 0 (eof) or -1 (error) */
{
int err = errno;
ASSERT(res <= 0);
(void) driver_select(port_num, ready_fd, ERL_DRV_READ|ERL_DRV_WRITE, 0);
clear_fd_data(ready_fd);
if (res == 0) {
if (driver_data[ready_fd].report_exit) {
CHLD_STAT_LOCK;
if (driver_data[ready_fd].alive) {
/*
* We have eof and want to report exit status, but the process
* hasn't exited yet. When it does report_exit_status() will
* driver_select() this fd which will make sure that we get
* back here with driver_data[ready_fd].alive == 0 and
* driver_data[ready_fd].status set.
*/
CHLD_STAT_UNLOCK;
return 0;
}
else {
int status = driver_data[ready_fd].status;
CHLD_STAT_UNLOCK;
/* We need not be prepared for stopped/continued processes. */
if (WIFSIGNALED(status))
status = 128 + WTERMSIG(status);
else
status = WEXITSTATUS(status);
driver_report_exit(driver_data[ready_fd].port_num, status); }
}
driver_failure_eof(port_num);
} else {
driver_failure_posix(port_num, err);
}
return 0;
}
void driver_report_exit(int ix, int status)
{
Port* prt = erts_drvport2port(ix);
Eterm* hp;
Eterm tuple;
Process *rp;
Eterm pid;
ErlHeapFragment *bp = NULL;
ErlOffHeap *ohp;
ErtsProcLocks rp_locks = 0;
ERTS_SMP_CHK_NO_PROC_LOCKS;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
pid = prt->connected;
ASSERT(is_internal_pid(pid));
rp = erts_pid2proc_opt(NULL, 0, pid, 0, ERTS_P2P_FLG_SMP_INC_REFC);
if (!rp)
return;
hp = erts_alloc_message_heap(3+3, &bp, &ohp, rp, &rp_locks);
tuple = TUPLE2(hp, am_exit_status, make_small(status));
hp += 3;
tuple = TUPLE2(hp, prt->id, tuple); erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined);
erts_smp_proc_unlock(rp, rp_locks);
erts_smp_proc_dec_refc(rp);
}
于是我们收到{Port, {exit_staus, Staus}}事件。
有点复杂吧,不过挺优雅的。记住信号处理函数里面不能做太耗时和调用有害的api。还有会有大量的退出事件发生,让调度器来调度这个事情比较公平,避免系统在处理退出处理上投入!
erts运行的时候会初始化smp_sig_notify,开启一个信号处理线程,在这个线程里面做具体的信号处理。
static void
init_smp_sig_notify(void)
{
erts_smp_thr_opts_t thr_opts = ERTS_SMP_THR_OPTS_DEFAULT_INITER;
thr_opts.detached = 1;
if (pipe(sig_notify_fds) < 0) {
erl_exit(ERTS_ABORT_EXIT,
"Failed to create signal-dispatcher pipe: %s (%d)\n",
erl_errno_id(errno),
errno);
}
/* Start signal handler thread */
erts_smp_thr_create(&sig_dispatcher_tid,
signal_dispatcher_thread_func,
NULL,
&thr_opts);
}
static void *
signal_dispatcher_thread_func(void *unused)
{
int initialized = 0;
#if !CHLDWTHR
int notify_check_children = 0;
#endif
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_set_thread_name("signal_dispatcher");
#endif
erts_thread_init_fp_exception();
while (1) {
char buf[32];
int res, i;
/* Block on read() waiting for a signal notification to arrive... */
res = read(sig_notify_fds[0], (void *) &buf[0], 32);
if (res < 0) {
if (errno == EINTR)
continue;
erl_exit(ERTS_ABORT_EXIT,
"signal-dispatcher thread got unexpected error: %s (%d)\n",
erl_errno_id(errno),
errno);
}
for (i = 0; i < res; i++) {
/*
* NOTE 1: The signal dispatcher thread should not do work
* that takes a substantial amount of time (except
* perhaps in test and debug builds). It needs to
* be responsive, i.e, it should only dispatch work
* to other threads.
*
* NOTE 2: The signal dispatcher thread is not a blockable
* thread (i.e., it hasn't called
* erts_register_blockable_thread()). This is
* intentional. We want to be able to interrupt
* writing of a crash dump by hitting C-c twice.
* Since it isn't a blockable thread it is important
* that it doesn't change the state of any data that
* a blocking thread expects to have exclusive access
* to (unless the signal dispatcher itself explicitly
* is blocking all blockable threads).
*/
switch (buf[i]) {
case 0: /* Emulator initialized */
initialized = 1;
#if !CHLDWTHR
if (!notify_check_children)
#endif
break;
#if !CHLDWTHR
case 'C': /* SIGCHLD */
if (initialized)
erts_smp_notify_check_children_needed();
else
notify_check_children = 1;
break;
#endif
case 'I': /* SIGINT */
break_requested();
break;
case 'Q': /* SIGQUIT */
quit_requested();
break;
case '1': /* SIGUSR1 */
sigusr1_exit();
break;
#ifdef QUANTIFY
case '2': /* SIGUSR2 */
quantify_save_data(); /* Might take a substantial amount of
time, but this is a test/debug
build */
break;
#endif
default:
erl_exit(ERTS_ABORT_EXIT,
"signal-dispatcher thread received unknown "
"signal notification: '%c'\n",
buf[i]);
}
}
ERTS_SMP_LC_ASSERT(!ERTS_LC_IS_BLOCKING);
}
return NULL;
}
void
erts_sys_main_thread(void)
{
/* Become signal receiver thread... */
#ifdef ERTS_ENABLE_LOCK_CHECK
erts_lc_set_thread_name("signal_receiver");
#endif
smp_sig_notify(0); /* Notify initialized */
while (1) {
/* Wait for a signal to arrive... */
#ifdef DEBUG
int res =
#else
(void)
#endif
select(0, NULL, NULL, NULL, NULL);
ASSERT(res < 0);
ASSERT(errno == EINTR);
}
}
因为外部的程序是fork exec来执行的,所以退出的时候erts进程就会受到SIGCHLD信号。
static int spawn_init()
{
...
sys_sigset(SIGCHLD, onchld); /* Reap children */
...
}
onchld就会被调用
static RETSIGTYPE onchld(int signum)
{
#if CHLDWTHR
ASSERT(0); /* We should *never* catch a SIGCHLD signal */
#elif defined(ERTS_SMP)
smp_sig_notify('C');
#else
children_died = 1;
ERTS_CHK_IO_INTR(1); /* Make sure we don't sleep in poll */
#endif
}
static void
smp_sig_notify(char c)
{
int res;
do {
/* write() is async-signal safe (according to posix) */
res = write(sig_notify_fds[1], &c, 1);
} while (res < 0 && errno == EINTR);
if (res != 1) {
char msg[] =
"smp_sig_notify(): Failed to notify signal-dispatcher thread "
"about received signal";
(void) write(2, msg, sizeof(msg));
abort();
}
}
于是erts_smp_notify_check_children_needed()被调用。
void
erts_smp_notify_check_children_needed(void)
{
ErtsSchedulerData *esdp;
erts_smp_sched_lock();
for (esdp = schedulers; esdp; esdp = esdp->next)
esdp->check_children = 1;
if (block_multi_scheduling) {
/* Also blocked schedulers need to check children */
erts_smp_mtx_lock(&msched_blk_mtx);
for (esdp = schedulers; esdp; esdp = esdp->next)
esdp->blocked_check_children = 1;
erts_smp_cnd_broadcast(&msched_blk_cnd);
erts_smp_mtx_unlock(&msched_blk_mtx);
}
wake_all_schedulers();
erts_smp_sched_unlock();
}
这个函数设置调度器的check_children的标志 并且唤醒所有的调度器。
调度器的入口process_main我们来看下如何处理的:
Process *schedule(Process *p, int calls)
{
...
if (esdp->check_children) {
esdp->check_children = 0;
erts_smp_sched_unlock();
erts_check_children();
erts_smp_sched_lock();
}
...
}
调用erts_check_children。
void
erts_check_children(void)
{
(void) check_children();
}
static int check_children(void)
{
int res = 0;
int pid;
int status;
#ifndef ERTS_SMP
if (children_died)
#endif
{
sys_sigblock(SIGCHLD);
CHLD_STAT_LOCK;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
note_child_death(pid, status);
#ifndef ERTS_SMP
children_died = 0;
#endif
CHLD_STAT_UNLOCK;
sys_sigrelease(SIGCHLD);
res = 1;
}
return res;
}
static void note_child_death(int pid, int status)
{
ErtsSysReportExit **repp = &report_exit_list;
ErtsSysReportExit *rep = report_exit_list;
while (rep) {
if (pid == rep->pid) {
*repp = rep->next;
ERTS_REPORT_EXIT_STATUS(rep, status);
break;
}
repp = &rep->next;
rep = rep->next;
}
}
static ERTS_INLINE void
report_exit_status(ErtsSysReportExit *rep, int status)
{
Port *pp;
#ifdef ERTS_SMP
CHLD_STAT_UNLOCK;
#endif
pp = erts_id2port_sflgs(rep->port,
NULL,
0,
ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP);
#ifdef ERTS_SMP
CHLD_STAT_LOCK;
#endif
if (pp) {
if (rep->ifd >= 0) {
driver_data[rep->ifd].alive = 0;
driver_data[rep->ifd].status = status;
(void) driver_select((ErlDrvPort) internal_port_index(pp->id),
rep->ifd,
DO_READ,
1);
}
if (rep->ofd >= 0) {
driver_data[rep->ofd].alive = 0;
driver_data[rep->ofd].status = status;
(void) driver_select((ErlDrvPort) internal_port_index(pp->id),
rep->ofd,
DO_WRITE,
1); }
erts_port_release(pp);
}
erts_free(ERTS_ALC_T_PRT_REP_EXIT, rep);
}
移除对该port的监视 销毁port.
static void ready_input(ErlDrvData e, ErlDrvEvent ready_fd)
{
...
res = read(ready_fd, read_buf, ERTS_SYS_READ_BUF_SZ);
if (res < 0) {
if ((errno != EINTR) && (errno != ERRNO_BLOCK))
port_inp_failure(port_num, ready_fd, res);
}
else if (res == 0)
port_inp_failure(port_num, ready_fd, res);
else
driver_output(port_num, (char*) read_buf, res);
erts_free(ERTS_ALC_T_SYS_READ_BUF, (void *) read_buf);
}....
}
static int port_inp_failure(int port_num, int ready_fd, int res)
/* Result: 0 (eof) or -1 (error) */
{
int err = errno;
ASSERT(res <= 0);
(void) driver_select(port_num, ready_fd, ERL_DRV_READ|ERL_DRV_WRITE, 0);
clear_fd_data(ready_fd);
if (res == 0) {
if (driver_data[ready_fd].report_exit) {
CHLD_STAT_LOCK;
if (driver_data[ready_fd].alive) {
/*
* We have eof and want to report exit status, but the process
* hasn't exited yet. When it does report_exit_status() will
* driver_select() this fd which will make sure that we get
* back here with driver_data[ready_fd].alive == 0 and
* driver_data[ready_fd].status set.
*/
CHLD_STAT_UNLOCK;
return 0;
}
else {
int status = driver_data[ready_fd].status;
CHLD_STAT_UNLOCK;
/* We need not be prepared for stopped/continued processes. */
if (WIFSIGNALED(status))
status = 128 + WTERMSIG(status);
else
status = WEXITSTATUS(status);
driver_report_exit(driver_data[ready_fd].port_num, status); }
}
driver_failure_eof(port_num);
} else {
driver_failure_posix(port_num, err);
}
return 0;
}
void driver_report_exit(int ix, int status)
{
Port* prt = erts_drvport2port(ix);
Eterm* hp;
Eterm tuple;
Process *rp;
Eterm pid;
ErlHeapFragment *bp = NULL;
ErlOffHeap *ohp;
ErtsProcLocks rp_locks = 0;
ERTS_SMP_CHK_NO_PROC_LOCKS;
ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));
pid = prt->connected;
ASSERT(is_internal_pid(pid));
rp = erts_pid2proc_opt(NULL, 0, pid, 0, ERTS_P2P_FLG_SMP_INC_REFC);
if (!rp)
return;
hp = erts_alloc_message_heap(3+3, &bp, &ohp, rp, &rp_locks);
tuple = TUPLE2(hp, am_exit_status, make_small(status));
hp += 3;
tuple = TUPLE2(hp, prt->id, tuple); erts_queue_message(rp, &rp_locks, bp, tuple, am_undefined);
erts_smp_proc_unlock(rp, rp_locks);
erts_smp_proc_dec_refc(rp);
}
于是我们收到{Port, {exit_staus, Staus}}事件。
有点复杂吧,不过挺优雅的。记住信号处理函数里面不能做太耗时和调用有害的api。还有会有大量的退出事件发生,让调度器来调度这个事情比较公平,避免系统在处理退出处理上投入!
发表评论
-
OTP R14A今天发布了
2010-06-17 14:36 2700以下是这次发布的亮点,没有太大的性能改进, 主要是修理了很多B ... -
R14A实现了EEP31,添加了binary模块
2010-05-21 15:15 3053Erlang的binary数据结构非常强大,而且偏向底层,在作 ... -
如何查看节点的可用句柄数目和已用句柄数
2010-04-08 03:31 4829很多同学在使用erlang的过程中, 碰到了很奇怪的问题, 后 ... -
获取Erlang系统信息的代码片段
2010-04-06 21:49 3490从lib/megaco/src/tcp/megaco_tcp_ ... -
iolist跟list有什么区别?
2010-04-06 20:30 6548看到erlang-china.org上有个 ... -
erlang:send_after和erlang:start_timer的使用解释
2010-04-06 18:31 8412前段时间arksea 同学提出这个问题, 因为文档里面写的很不 ... -
Latest news from the Erlang/OTP team at Ericsson 2010
2010-04-05 19:23 2024参考Talk http://www.erlang-factor ... -
对try 异常 运行的疑问,为什么出现两种结果
2010-04-05 19:22 2858郎咸武<langxianzhe@163.com> ... -
Erlang ERTS Async基础设施
2010-03-19 00:03 2538其实Erts的Async做的很不错的, 相当的完备, 性能又高 ... -
CloudI 0.0.9 Released, A Cloud as an Interface
2010-03-09 22:32 2489基于Erlang的云平台 看了下代码 质量还是不错的 完成了不 ... -
Memory matters - even in Erlang (再次说明了了解内存如何工作的必要性)
2010-03-09 20:26 3466原文地址:http://www.lshift.net/blog ... -
Some simple examples of using Erlang’s XPath implementation
2010-03-08 23:30 2061原文地址 http://www.lshift.net/blog ... -
lcnt 环境搭建
2010-02-26 16:19 2626抄书:otp_doc_html_R13B04/lib/tool ... -
Erlang强大的代码重构工具 tidier
2010-02-25 16:22 2493Jan 29, 2010 We are very happy ... -
[Feb 24 2010] Erlang/OTP R13B04 has been released
2010-02-25 00:31 1398Erlang/OTP R13B04 has been rele ... -
R13B04 Installation
2010-01-28 10:28 1409R13B04后erlang的源码编译为了考虑移植性,就改变了编 ... -
Running tests
2010-01-19 14:51 1502R13B03以后 OTP的模块加入了大量的测试模块,这些模块都 ... -
R13B04在细化Binary heap
2010-01-14 15:11 1515从github otp的更新日志可以清楚的看到otp R13B ... -
R13B03 binary vheap有助减少binary内存压力
2009-11-29 16:07 1673R13B03 binary vheap有助减少binary内存 ... -
erl_nif 扩展erlang的另外一种方法
2009-11-26 01:02 3238我们知道扩展erl有2种方法, driver和port. 这2 ...
相关推荐
系统调度策略包括处理超时的定时器、子进程退出、PORT的IO事件等。当没有活跃进程时,系统调度器会阻塞在底层的IO操作,等待事件触发。 总的来说,Erlang VM的设计理念是提供一个高效、可靠且易于扩展的平台,用于...
总结来说,`os:cmd/1`适合执行简单命令并获取标准输出,而`erlang:open_port/2`则适用于更复杂的场景,如需要错误信息、退出状态码或者控制进程交互。选择哪种方法取决于你的具体需求和对性能的影响。
这篇文章主要讲解了如何在Erlang和Python之间实现通信,这对于构建分布式系统或者利用不同语言的优势处理特定任务非常有用。Erlang以其并发特性和稳定性著称,而Python则因为其丰富的库和易读性而受到青睐。通过两者...
基于COMSOL的电磁场与光学仿真:多极分解通用模型探讨石墨烯临界耦合光吸收与费米能级可调性,COMSOL 多极分解,分方向多级展开通用模型,电磁场,面上箭头,透射率光学 BIC 仿真。 COMSOL 准 BIC控制石墨烯临界耦合光吸收。 COMSOL 光学仿真,石墨烯,光吸收,费米能级可调 ,关键词:COMSOL; 多极分解; 分方向多级展开通用模型; 电磁场; 面上箭头; 透射率; BIC 仿真; 准 BIC; 控制石墨烯; 临界耦合光吸收; 光学仿真; 石墨烯; 光吸收; 费米能级可调。,COMSOL多极分解法仿真石墨烯光吸收特性及费米能级调控
Matlab Simulink下的单相光伏储能模型:可再生能源发电与蓄电池储能系统建模与仿真,涵盖MPPT电导增量法及并网技术,Matlab simulink模型,单相光伏储能模型可再生能源发电 太阳能发电蓄电池储能建模与仿真 可调节光照强度,采用MPPT电导增量法最大功率点跟踪双向DC DC蓄电池储能及补偿。 额定功率2500w,并网等级311v有效值220v ,Matlab Simulink模型; 单相光伏储能模型; 可再生能源发电; 太阳能发电; 蓄电池储能建模与仿真; 调节光照强度; MPPT电导增量法; 最大功率点跟踪; 双向DC DC; 额定功率2500w; 并网等级311v; 有效值220v,基于Matlab Simulink的单相光伏储能模型:MPPT跟踪与蓄电池储能补偿研究
包含项目论文和毕业答辩PPT,详情请看博客:https://blog.csdn.net/2401_87429224/article/details/145231201 论文主要包括以下内容: 1、中英文摘要; 2、目录; 3、绪论,包括背景、意义、开发工具、国内外现状等; 4、系统分析,包括可行性分析、设计原则、需求分析、业务流程分析等; 5、系统设计,包括功能设计、数据库设计等; 6、系统实现,包括各模块实现; 7、软件测试,包括测试环境、测试条件、运行情况等。
"图腾柱PFC电路仿真研究:电压电流双闭环PI控制下的动态响应与稳定性验证及主电路设计说明",图腾柱(totem pole)PFC电路仿真,采用电压电流双闭环PI控制。 输出特性好。 仿真中模拟了给定电压变化时的动态响应情况。 可验证闭环控制的稳定性。 另也有图腾柱PFC主电路参数的设计说明 matlab simulink plecs等环境 ,totem_pole; PFC电路仿真; 电压电流双闭环PI控制; 动态响应; 闭环控制稳定性; 主电路参数设计; Matlab Simulink; PLECS。,"图腾柱PFC电路仿真与参数设计研究,电压电流双闭环PI控制稳定性的验证"
2025年材料员网络培训考试题库及答案.docx
**Qt CPP多列时间轴控件:事件线发展故事大纲的可视化编辑器**,Qt CPP实现的多列时间轴控件、可与多段字符串格式自由转、也可手动添加列表项、专门用来以时间轴作为事件线发展顺序的故事大纲。 时间可输入任意内容,不限于时间,每一时间段允许多列,即多个文字节点,行与行、列与列 之间任意拖拽更顺序,可与文字自由转,按需修改快捷键,所有的编辑可撤销,美观的调整动画。 源码: 使用Qt5.13.1_MinGW编译通过。 ,Qt CPP时间轴控件; 多列时间节点; 自由转换字符串格式; 手动添加列表项; 时间轴事件线; 拖拽更换顺序; 快捷键自定义; 编辑可撤销; 动画调整。,基于Qt C++的多功能多列时间轴故事线管理控件
===下载后有不懂的可以私信我。==== 红外解码与红外遥控是电子工程领域中常见的一种技术,主要应用于家用电器、智能家居设备以及遥控玩具等。本文将深入探讨这两个概念,并结合"28.红外遥控解码数码管显示实验"这一主题,为你提供丰富的学习内容。 我们要理解红外(Infrared,简称IR)的基本原理。红外是一种电磁波,频率范围在300GHz到400THz之间,位于可见光的红色光谱之外。在遥控系统中,红外通信通常采用的是近红外频段,即约940nm左右的波长。它具有传输距离近、功耗低、抗干扰能力较弱的特点,适用于室内短距离通信。 红外遥控系统由两部分组成:发射器(遥控器)和接收器(解码器)。发射器通常包含一个微控制器和红外LED,用于编码并发射控制信号。这些信号是通过特定的编码方式,如RC5、NEC、SIRC等编码协议发送的,以确保数据的准确性和唯一性。接收器则含有一个红外光电传感器,用于捕捉这些信号并进行解码。 红外解码是接收器的核心功能。当接收到红外信号后,解码器会对其进行解调,然后解析出编码的指令。解码过程包括信号放大、滤波、峰值检测和脉冲计数等步骤。不同的编码协议有不同的解码方法,例如NEC协议的解码需要识别前导码、地址码、命令码和校验码等组成部分。 数码管显示在红外遥控实验中扮演了重要角色。数码管,也称为数字管或七段显示器,可以用来显示数字、字母和一些特殊字符。在红外遥控解码实验中,一旦解码成功,通常会通过数码管将接收到的指令代码或状态信息呈现出来,便于观察和调试。 "28.红外遥控解码数码管显示实验"可能是一个实践项目,旨在帮助学习者亲手搭建一个简单的红外遥控系统。这个实验可能涵盖了以下内容: 1. 硬件搭建:包括红外接收模块、微控制器(如Arduino或AVR)、数码管及其驱动电路。 2. 软件编程:编写微控制器的程序来实现红外信号的接收、解码以及数码管的显示控制。这涉及到对红外解码协议的理解和编程实现。 3. 实验操作:调整遥控器,发送不同指令,观察数码管的反馈,验证解码的正确性。 通过这样的实验,你可以亲身体验红外遥控系统的完整流程,加深对红外解码原理和实践应用的理解。同时,这也是提升动手能力和解决问题能力的良好机会。 红外解码和红外遥控是电子工程师必备的技能之一。掌握这些知识不仅可以让你理解和设计自己的遥控系统,还为进入更高级的嵌入式系统开发打下基础。希望这个资源能对你在红外学习的道路上有所帮助,祝你在探索科技的旅程中不断进步。。内容来源于网络分享,如有侵权请联系我删除。
资源说明: 1:csdn平台资源详情页的文档预览若发现'异常',属平台多文档切片混合解析和叠加展示风格,请放心使用。 2:29页图文详解文档(从零开始项目全套环境工具安装搭建调试运行部署,保姆级图文详解),旨在为更多的人甚至零基础的人也能运行、使用和学习。 3:配套毕业论文,万字长文,word文档,支持二次编辑。 4:配套答辩ppt,pptx格式,支持二次编辑。 5:工具环境、ppt参考模板、相关电子教程、视频教学资源分享。 6:资源项目源码均已通过严格测试验证,保证能够正常运行,本项目仅用作交流学习参考,请切勿用于商业用途。 7:项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通。 内容概要: 本系统基于 B/S 网络结构,在 IDEA 中开发。服务端用 Java 并借 Spring Boot 框架搭建后台。前台采用支持 HTML5 的 VUE 框架。用 MySQL 存储数据,可靠性强。 能学到什么: 使用Spring Boot搭建后台。VUE 框架构建前端交互界面、前后端数据交互、MySQL管理数据、从零开始环境搭建、调试、运行、打包、部署流程。
【Unity动画烘焙插件】GPU ECS Animation Baker
MATLAB轴承动力学模拟:滚动轴承故障建模与ODE45数值计算(含正常、外圈故障、内圈故障及滚动体故障分析),MATLAB轴承动力学代码(正常、外圈故障、内圈故障、滚动体故障),根据滚动轴承故障机理建模(含数学方程建立和公式推导)并在MATLAB中采用ODE45进行数值计算。 可模拟不同轴承故障类型,输出时域加速度波形、滚道接触力、相图。 ,核心关键词:MATLAB; 轴承动力学代码; 故障机理建模; 数学方程建立; 公式推导; ODE45数值计算; 不同轴承故障类型; 时域加速度波形; 滚道接触力; 相图。,MATLAB滚动轴承故障动力学模型及数值模拟分析系统
原作者:刘金琨教授
《2024最强Java面试八股文》是一份针对Java面试的全面指南,内容涵盖JVM、MQ、MyBatis、MySQL、Redis、Spring Boot、Spring Cloud以及设计模式等多个方面。这份资料旨在帮助求职者系统地复习和掌握Java及相关技术的核心知识点,从而在面试中取得更好的成绩。 该资源不仅适用于校招,也适用于社招。无论您是一名即将毕业的学生还是已经有一定工作经验的开发者,都可以通过这份资料深入了解Java面试的常见考点和难点,提升自己的技能和知识储备。 在内容上,这份资料对每个技术点都进行了深入浅出的讲解,并配有大量的实例和案例分析。此外,还提供了丰富的题目和答案,帮助您更好地掌握每个知识点的运用。 总的来说,《2024最强Java面试八股文》是一份不可多得的Java面试宝典,它系统地梳理了Java面试的核心知识点,并提供了一系列的实战技巧和经验。无论您是正在准备面试的求职者,还是希望深入了解Java技术的开发者,都可以从这份资料中受益匪浅。
内容概要:本文提出了一个名为LDP(Language-driven DP)的新框架,旨在利用预训练的对比语言-图像模型CLIP来估计双像素(DP)图像对的模糊图,从而实现高质量的无监督去模糊任务。研究解决了以往需要额外数据进行监督的问题,首次通过文本提示引导CLIP来估计模糊程度并应用自定义格式处理立体图像对,进一步提出了三种损失函数和一个先验注意力模块以确保恢复图像的质量。该方法通过广泛的实验验证,在标准测试集上取得了当前最优性能。 适合人群:计算机视觉研究人员以及从事深度学习和图像处理的技术人员。 使用场景及目标:用于从具有视差相关模糊问题的双像素传感器获取到的图片对恢复清晰的全焦点图片,尤其适用于单张拍摄情况下的图像修复工作。此外,该研究可以推动更多利用大规模预训练视觉语言模型应用于低级视觉任务的可能性。 其他说明:文中还提供了详细的实证比较和其他现有方法的优势分析,证明了所提出的架构能够提高去模糊效果的同时显著降低了推理时间。同时给出了模型的不同组件及其损失设置的消融实验细节与模型局限性的探讨,补充材料中有更深入的具体实验参数介绍及更多的定性和定量评估。
CSDN海神之光上传的全部代码均可运行,亲测可用,直接替换数据即可,适合小白; 1、代码压缩包内容 主函数:Main .m; 调用函数:其他m文件;无需运行 运行结果效果图; 2、代码运行版本 Matlab 2019b;若运行有误,根据提示修改;若不会,可私信博主; 3、运行操作步骤 步骤一:将所有文件放到Matlab的当前文件夹中; 步骤二:双击打开除Main.m的其他m文件; 步骤三:点击运行,等程序运行完得到结果; 4、仿真咨询 如需其他服务,可私信博主或扫描博主博客文章底部QQ名片; 4.1 CSDN博客或资源的完整代码提供 4.2 期刊或参考文献复现 4.3 Matlab程序定制 4.4 科研合作
Simulink光伏储能VSG仿真模型:完美波形运行,实现光储一次调频、削峰填谷及直流母线电压控制功能,simulink仿真模型光伏储能VSG,加电容,正常运行,波形完美一一光储一次调频、储能削峰填谷、 直流母线电压控制。 2018b及以上都可运行哈 ,simulink仿真;光伏储能;VSG;加电容;正常运行;波形完美;光储一次调频;储能削峰填谷;直流母线电压控制;2018b及以上版本。,Simulink光伏储能VSG模型:电容增强与优化调频调压功能解析
三相VIENNA整流器仿真研究:T型整流器双闭环PI控制及中点电位平衡控制策略,SPWM调制与高效能表现,三相VIENNA整流器仿真 matlab仿真 T型vienna整流器仿真 双闭环PI控制,中点电位平衡控制,SPWM调制,三相锁相环。 图3为三相电流波形,图4THD为1.01%,电感仅为2mL。 图4直流侧电压波形,能准确跟踪给定值750V,图5为直流母线侧上下电容电压,中点电位波动极小。 功率因数为99%以上。 ,三相VIENNA整流器仿真; Matlab仿真; T型vienna整流器仿真; 双闭环PI控制; 中点电位平衡控制; SPWM调制; 三相锁相环; 电流波形; THD; 电感; 直流侧电压波形; 直流母线侧电容电压; 功率因数。,三相Vienna整流器:仿真、控制与性能优化分析
===下载后有不懂的可以私信我。==== 时间序列预测是一种重要的数据分析技术,尤其在金融、气象学、工程控制等领域有着广泛的应用。它通过对历史数据的分析,寻找规律并预测未来的趋势。在这个项目中,我们将关注一种基于最小均方(LMS)算法的时间序列预测方法,该方法利用了MATLAB编程环境来实现。MATLAB是数学计算和数据分析的强大工具,它提供了丰富的库函数和优化工具,使得复杂算法的实现变得相对简单。 最小均方(Least Mean Squares,LMS)算法是自适应滤波理论中的一个关键算法,最初由Widrow和Hoff在1960年提出。它的主要目标是在不断更新滤波器权重的过程中,最小化预测误差的平方和。在时间序列预测中,LMS算法可以用来调整模型参数,以适应数据的变化,从而提高预测的准确性。 在MATLAB中实现LMS算法,首先需要理解基本的步骤: 1. **数据预处理**:对原始时间序列数据进行清洗,去除异常值,可能还需要进行归一化或标准化处理,以消除量纲影响。 2. **模型设定**:选择合适的预测模型,例如线性模型、ARIMA模型或者神经网络模型。LMS算法通常用于在线学习,适合于线性模型。 3. **初始化**:设定滤波器的初始权重,以及学习速率和步长等参数。 4. **迭代过程**:对于每一个新的数据点,用当前滤波器权重计算预测值,然后根据预测误差更新权重。LMS算法的关键在于误差的梯度下降更新规则。 5. **误差计算**:计算预测值与实际值之间的误差,通常使用均方误差(MSE)作为评估指标。 6. **性能评估**:通过比较预测结果与实际值,评估模型的预测性能。常用的评估指标有均方根误差(RMSE)、平均绝对误差(MAE)等。 在这个项目中,MATLAB源码应该包含了以上所有步骤的实现。通过运行代码,你可以观察到预测结果和实际值的对比,以及随着迭代次数增加,预测性能的变化。这有助于理解LMS算法如何逐步优化模型参数,提高预测精度。 需要注意的是,LMS算法虽然简单且易于实现,但在噪声较大的环境中可能会收敛较慢,且可能存在稳定性问题。为了改善这些问题,可以考虑使用改进版的LMS算法,如快速LMS(Fast LMS)、正常化LMS(Normalized LMS)或加速度LMS(Accelerated LMS)等。 在实际应用中,时间序列预测不仅仅是模型的选择和训练,还包括特征工程、模型验证和调参等多个环节。因此,理解并掌握LMS算法只是时间序列预测的一部分,还需要结合其他数据处理和预测技巧,才能构建出更强大的预测系统。这个MATLAB项目提供了一个很好的学习平台,帮助你深入理解LMS算法在时间序列预测中的应用,并通过实际操作提升技能。。内容来源于网络分享,如有侵权请联系我删除。