Erlang的port的释放过程

static void terminate_port(Port *prt)

{

    Eterm send_closed_port_id;

    Eterm connected_id = NIL /* Initialize to silence compiler */;

    erts_driver_t *drv;

 

    ERTS_SMP_CHK_NO_PROC_LOCKS;

    ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt));

 

    ASSERT(!prt->nlinks);

    ASSERT(!prt->monitors);

 

    if (prt->status & ERTS_PORT_SFLG_SEND_CLOSED) {

erts_port_status_band_set(prt, ~ERTS_PORT_SFLG_SEND_CLOSED);

send_closed_port_id = prt->id;

connected_id = prt->connected;

    }

    else {

send_closed_port_id = NIL;

    }

 

#ifdef ERTS_SMP

    erts_cancel_smp_ptimer(prt->ptimer);

#else

    erts_cancel_timer(&prt->tm);

#endif

 

    drv = prt->drv_ptr;

    if ((drv != NULL) && (drv->stop != NULL)) {

int fpe_was_unmasked = erts_block_fpe();

(*drv->stop)((ErlDrvData)prt->drv_data);

        /* 若有则调用port的driver的stop函数 */

erts_unblock_fpe(fpe_was_unmasked);

#ifdef ERTS_SMP

if (prt->xports)

   erts_smp_xports_unlock(prt);

ASSERT(!prt->xports);

#endif

    }

    if(drv->handle != NULL) {

erts_smp_mtx_lock(&erts_driver_list_lock);

erts_ddll_decrement_port_count(drv->handle); 

        /* 若driver使用了动态链接库或共享库,则减少其引用计数 */

erts_smp_mtx_unlock(&erts_driver_list_lock);

    }

    stopq(prt);        /* clear queue memory */

    if(prt->linebuf != NULL){

erts_free(ERTS_ALC_T_LINEBUF, (void *) prt->linebuf);

        /* 释放用于保存未集齐的数据的线性缓冲区 */

prt->linebuf = NULL;

    }

    if (prt->bp != NULL) {

free_message_buffer(prt->bp);

        /* 释放堆分片 */

prt->bp = NULL;

prt->data = am_undefined;

    }

 

    if (prt->psd)

erts_free(ERTS_ALC_T_PRTSD, prt->psd);

        /* 释放port特定数据结构占用的内存 */

 

    kill_port(prt);

 

    /*

     * We don't want to send the closed message until after the

     * port has been removed from the port table (in kill_port()).

     */

    if (is_internal_port(send_closed_port_id))

deliver_result(send_closed_port_id, connected_id, am_closed);

 

    ASSERT(prt->dist_entry == NULL);

}

 

static ERTS_INLINE void kill_port(Port *pp)

{

    ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(pp));

    erts_port_task_free_port(pp);

    ASSERT(pp->status & ERTS_PORT_SFLGS_DEAD);

}

 

void erts_port_task_free_port(Port *pp)

{

    ErtsRunQueue *runq;

    int port_is_dequeued = 0;

 

    ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(pp));

    ASSERT(!(pp->status & ERTS_PORT_SFLGS_DEAD));

    runq = erts_port_runq(pp);

    ASSERT(runq);

    ERTS_PT_CHK_PRES_PORTQ(runq, pp);

    if (pp->sched.exe_taskq) {

/* I (this thread) am currently executing this port, free it

  when scheduled out... */

ErtsPortTask *ptp = port_task_alloc();

erts_smp_port_state_lock(pp);

pp->status &= ~ERTS_PORT_SFLG_CLOSING;

pp->status |= ERTS_PORT_SFLG_FREE_SCHEDULED;

erts_may_save_closed_port(pp);

erts_smp_port_state_unlock(pp);

ERTS_SMP_LC_ASSERT(erts_smp_atomic_read(&pp->refc) > 1);

ptp->type = ERTS_PORT_TASK_FREE;

ptp->event = (ErlDrvEvent) -1;

ptp->event_data = NULL;

set_handle(ptp, NULL);

push_task(pp->sched.exe_taskq, ptp);

ERTS_PT_CHK_PRES_PORTQ(runq, pp);

erts_smp_runq_unlock(runq);

    }

    else {

        /* 仅仅分析这个简单的场景以说明问题,另外一个场景类似 */

ErtsPortTaskQueue *ptqp = pp->sched.taskq;

if (ptqp) {

   dequeue_port(runq, pp);

   ERTS_PORT_NOT_IN_RUNQ(pp);

   port_is_dequeued = 1;

}

erts_smp_port_state_lock(pp);

pp->status &= ~ERTS_PORT_SFLG_CLOSING;

pp->status |= ERTS_PORT_SFLG_FREE_SCHEDULED;

        /* port的状态被更改为了ERTS_PORT_SFLG_FREE_SCHEDULED,它也是ERTS_PORT_SFLGS_DEAD的一种 */

erts_may_save_closed_port(pp);

        /* 能够让erts_dead_ports_ptr保存已经退出的port,则port在退出时一定走到了这里,我们其实仅需要关注在这里之后是否有port资源泄露即可 */

erts_smp_port_state_unlock(pp);

#ifdef ERTS_SMP

erts_smp_atomic_dec(&pp->refc); /* Not alive */

#endif

ERTS_SMP_LC_ASSERT(erts_smp_atomic_read(&pp->refc) > 0); /* Lock */

handle_remaining_tasks(runq, pp); /* May release runq lock */

        /*这个函数将释放挂在port上的所有ErtsPortTask,port能够执行的各项任务,也被像消息一样发给port,由port异步执行,这里将释放port的任务队列上的所有ErtsPortTask任务的数据结构*/

ASSERT(!pp->sched.exe_taskq && (!ptqp || !ptqp->first));

pp->sched.taskq = NULL;

ERTS_PT_CHK_PRES_PORTQ(runq, pp);

#ifndef ERTS_SMP

ASSERT(pp->status & ERTS_PORT_SFLG_PORT_DEBUG);

erts_port_status_set(pp, ERTS_PORT_SFLG_FREE);

        /* port的状态又被改为了ERTS_PORT_SFLG_FREE,它也是ERTS_PORT_SFLGS_DEAD的一种,但设置为这个状态后,表名port原先的描述符Port数据结构可以被重新分配给一个新建立的port了,因为之前已经触发了erts_may_save_closed_port,因此按照顺序执行流的执行,除非发生异常,否则必然会到此处 */

#endif

erts_smp_runq_unlock(runq);

 

if (erts_system_profile_flags.runnable_ports && port_is_dequeued) {

       profile_runnable_port(pp, am_inactive);

    }

 

if (ptqp)

   port_taskq_free(ptqp);

        /*释放port的任务队列*/

    }

}

由此可见port的释放其实没有那么复杂,虚拟机本身就有port数量限制,每次的port释放都仅仅将port的描述符设置为ERTS_PORT_SFLG_FREE以进行复用,而不会真正释放数据结构。

再来看看用于获取空闲port描述符的get_free_port:

io.c

 

static int get_free_port(void)

{

    Uint num;

    Uint tries = erts_max_ports;

    Port* port;    

 

    erts_smp_spin_lock(&get_free_port_lck);

    num = last_port_num + 1;

    for (;; ++num) {

port = &erts_port[num & erts_port_tab_index_mask];

 

erts_smp_port_state_lock(port);

if (port->status & ERTS_PORT_SFLG_FREE) {

   last_port_num = num;

   erts_smp_spin_unlock(&get_free_port_lck);

   break;

}

erts_smp_port_state_unlock(port);

 

if (--tries == 0) {

   erts_smp_spin_unlock(&get_free_port_lck);

   return -1;

}

    }

    port->status = ERTS_PORT_SFLG_INITIALIZING;

#ifdef ERTS_SMP

    ERTS_SMP_LC_ASSERT(erts_smp_atomic_read(&port->refc) == 0);

    erts_smp_atomic_set(&port->refc, 2); /* Port alive + lock */

#endif

    erts_smp_port_state_unlock(port);

    return num & port_num_mask;

}

get_free_port用于取得一个空闲port描述符,它将遍历erts_port记录的所有port描述符,然后从中取得一个状态为ERTS_PORT_SFLG_FREE的描述符。

由此可见port的分配与释放都不会引发port描述符的内存分配与释放,仅仅会复用一个而已。

至此,问题原因已经基本清楚了,erlang:ports()和erlang:processes()将返回在某个时刻的端口和进程的快照,这样的结果更加一致,因为时刻的快照比时间间隔的快照更加精准。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值