------------------ port 篇 -------------
open_port/2这个是由bif实现, 源文件在erl_bif_port.c中, 函数是BIF_RETTYPE open_port_2(BIF_ALIST_2)
其中:
#define BIF_RETTYPE Eterm
#define BIF_ALIST_2 Process* A__p, Eterm A_1, Eterm A_2
此函数调用了同文件中的
static int
open_port(Process* p, Eterm name, Eterm settings, int *err_nump)
函数.
name 对应 open_port/2的第一个参数PortName
setting 对应 open_port/2的第二个参数PortSettings
其中:
PortSettings = [Opt]
Opt = {packet, N} | stream | {line, L} | {cd, Dir} | {env, Env} | exit_status | use_stdio | nouse_stdio | stderr_to_stdout | in | out | binary | eof
N = 1 | 2 | 4
L = int()
Dir = string()
Env = [{Name, Val}]
Name = string()
Val = string() | false
这些port的设置都由open_port函数Parse the settings注释后面的代码解析. erl_bif_port.c: 475行
第一个参数name的解析由open_port函数Parse the first argument and start the appropriate driver注释后面的代码解析. erl_bif_port.c: 590行
此处解析第一个参数name,并开启正确的driver
PortName = {spawn, Command} | {fd, In, Out}
分两种情况: spawn出来的进程, 使用文件描述符
spawn_driver_entry外部定义, spawn_driver_entry是名为spawn的driver的入口结构体. 它在linux平台上的定义在emulator/sys/unix/sys.c下
struct erl_drv_entry spawn_driver_entry = {
spawn_init,
spawn_start,
stop,
output,
ready_input,
ready_output,
"spawn",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
ERL_DRV_EXTENDED_MARKER,
ERL_DRV_EXTENDED_MAJOR_VERSION,
ERL_DRV_EXTENDED_MINOR_VERSION,
ERL_DRV_FLAG_USE_PORT_LOCKING
};
erl_drv_entry这个结构体的定义的emulator/beam/erl_driver.h. 结构体的成员包括port初始化, 开始,停止, 取/出数据的各种函数指针(因为运行的平台不同,所以用指针来指定策略).
真正干活的是io.c下面的
/*
Opens a driver.
Returns the non-negative port number, if successful.
If there is an error, -1 or -2 or -3 is returned. -2 means that
there is valid error information in 'errno'.
Returning -3 means that an error in the given options was detected.
The driver start function must obey the same conventions.
*/
int
erts_open_driver(ErlDrvEntry* driver, /* Pointer to driver entry. */
Eterm pid, /* Current process. */
char* name, /* Driver name. */
SysDriverOpts* opts, /* Options. */
int *error_number_ptr) /* errno in case -2 is returned */
函数. io.c: 505行
步骤:
1. 获取一个free的port,取得一个port_num. 通过 static int get_free_port(void)函数来检查, 如果port的status属性为ERTS_PORT_S_FREE则这个port是free的
2. 检查同名的driver是否已经存在.如果存在,则用已有的driver,如果不存在则用默认的spawn/fd driver
3. drv_data = (*driver->start)((ErlDrvPort)port_num, name, opts); 初始化一些东西而已,比如driver名称,driver的io队列等
4. 用已有的名为spawn的driver启动driver, drv_data = (*driver->start)((ErlDrvPort)port_num, name, opts); (unix/sys.c)
5. clean up the port.
6. 返回port_num.
附:名为efile的driver,它的struct erl_drv_entry结构体定义在emulator/drivers/comm/efile_drv.c中, 如下:
struct erl_drv_entry efile_driver_entry = {
file_init,
file_start,
file_stop,
file_output,
NULL,
NULL,
"efile",
NULL,
NULL,
file_control,
file_timeout,
file_outputv,
file_async_ready,
file_flush,
NULL,
NULL,
ERL_DRV_EXTENDED_MARKER,
ERL_DRV_EXTENDED_MAJOR_VERSION,
ERL_DRV_EXTENDED_MINOR_VERSION,
ERL_DRV_FLAG_USE_PORT_LOCKING,
NULL
};
open_port/2这个是由bif实现, 源文件在erl_bif_port.c中, 函数是BIF_RETTYPE open_port_2(BIF_ALIST_2)
其中:
#define BIF_RETTYPE Eterm
#define BIF_ALIST_2 Process* A__p, Eterm A_1, Eterm A_2
此函数调用了同文件中的
static int
open_port(Process* p, Eterm name, Eterm settings, int *err_nump)
函数.
name 对应 open_port/2的第一个参数PortName
setting 对应 open_port/2的第二个参数PortSettings
其中:
PortSettings = [Opt]
Opt = {packet, N} | stream | {line, L} | {cd, Dir} | {env, Env} | exit_status | use_stdio | nouse_stdio | stderr_to_stdout | in | out | binary | eof
N = 1 | 2 | 4
L = int()
Dir = string()
Env = [{Name, Val}]
Name = string()
Val = string() | false
这些port的设置都由open_port函数Parse the settings注释后面的代码解析. erl_bif_port.c: 475行
第一个参数name的解析由open_port函数Parse the first argument and start the appropriate driver注释后面的代码解析. erl_bif_port.c: 590行
此处解析第一个参数name,并开启正确的driver
PortName = {spawn, Command} | {fd, In, Out}
分两种情况: spawn出来的进程, 使用文件描述符
spawn_driver_entry外部定义, spawn_driver_entry是名为spawn的driver的入口结构体. 它在linux平台上的定义在emulator/sys/unix/sys.c下
struct erl_drv_entry spawn_driver_entry = {
spawn_init,
spawn_start,
stop,
output,
ready_input,
ready_output,
"spawn",
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
ERL_DRV_EXTENDED_MARKER,
ERL_DRV_EXTENDED_MAJOR_VERSION,
ERL_DRV_EXTENDED_MINOR_VERSION,
ERL_DRV_FLAG_USE_PORT_LOCKING
};
erl_drv_entry这个结构体的定义的emulator/beam/erl_driver.h. 结构体的成员包括port初始化, 开始,停止, 取/出数据的各种函数指针(因为运行的平台不同,所以用指针来指定策略).
真正干活的是io.c下面的
/*
Opens a driver.
Returns the non-negative port number, if successful.
If there is an error, -1 or -2 or -3 is returned. -2 means that
there is valid error information in 'errno'.
Returning -3 means that an error in the given options was detected.
The driver start function must obey the same conventions.
*/
int
erts_open_driver(ErlDrvEntry* driver, /* Pointer to driver entry. */
Eterm pid, /* Current process. */
char* name, /* Driver name. */
SysDriverOpts* opts, /* Options. */
int *error_number_ptr) /* errno in case -2 is returned */
函数. io.c: 505行
步骤:
1. 获取一个free的port,取得一个port_num. 通过 static int get_free_port(void)函数来检查, 如果port的status属性为ERTS_PORT_S_FREE则这个port是free的
2. 检查同名的driver是否已经存在.如果存在,则用已有的driver,如果不存在则用默认的spawn/fd driver
3. drv_data = (*driver->start)((ErlDrvPort)port_num, name, opts); 初始化一些东西而已,比如driver名称,driver的io队列等
4. 用已有的名为spawn的driver启动driver, drv_data = (*driver->start)((ErlDrvPort)port_num, name, opts); (unix/sys.c)
5. clean up the port.
6. 返回port_num.
附:名为efile的driver,它的struct erl_drv_entry结构体定义在emulator/drivers/comm/efile_drv.c中, 如下:
struct erl_drv_entry efile_driver_entry = {
file_init,
file_start,
file_stop,
file_output,
NULL,
NULL,
"efile",
NULL,
NULL,
file_control,
file_timeout,
file_outputv,
file_async_ready,
file_flush,
NULL,
NULL,
ERL_DRV_EXTENDED_MARKER,
ERL_DRV_EXTENDED_MAJOR_VERSION,
ERL_DRV_EXTENDED_MINOR_VERSION,
ERL_DRV_FLAG_USE_PORT_LOCKING,
NULL
};