经过上篇文章“linux下erlang关闭port同时也关掉与其连接的c语言进程”的分析,我们澄清了erlang关闭端口时,stdin会得到一个feof,进而关闭stdin。经过进一步的探究,发现有另两种新的解决方法:
第一:借助于linux下read函数,read函数原型:
ssize_t read(int fd,void *buf,size_t count)
函数返回值分为下面几种情况:
1、如果读取成功,则返回实际读到的字节数。这里又有两种情况:一是如果在读完count要求字节之前已经到达文件的末尾,那么实际返回的字节数将小于count值,但是仍然大于0;二是在读完count要求字节之前,仍然没有到达文件的末尾,这是实际返回的字节数等于要求的count值。
2、如果读取时已经到达文件的末尾,则返回0。
3、如果出错,则返回-1。
因此,当erlang关闭端口时,read(0,buf,len)会返回0,此该让c进程退出。具体做法是
#include <stdio.h>
#include <unistd.h>
typedef unsigned char byte;
typedef char int8;
int main() {
FILE * fp;
fp = fopen("ports.log", "a+");
fprintf(fp, "start...\n");
fflush(fp);
byte buf[256]={0};
int i = 0;
for(;;)
{
int8 len = buf[0];
//方法1:利用feof时,read返回0
if ((i=read(0, buf, 3)) <= 0)
return (i);
write(1, buf, 3);
}
附:erlang的port_close()关闭时,read返回0并不是erlang进程向c进程发了0字节的东西,因为经过实验是无法向stdin中写0字节的东西, Port ! {self(), {command, “”}}c进程并没有挂掉,推导出read并没有返回0。
第二、借助于struct _IO_FILE结构体中的_flags字段,该字段的含义_flags:true表示stdin关闭;_flags:false表示stdin打开;
|
It's in /usr/include/libio.h (which is included in stdio.h) and struct defintion is:
struct _IO_FILE { int _flags; /* High-order word is _IO_MAGIC; rest is flags. */ #define _IO_file_flags _flags /* The following pointers correspond to the C++ streambuf protocol. */ /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */ char* _IO_read_ptr; /* Current read pointer */ char* _IO_read_end; /* End of get area. */ char* _IO_read_base; /* Start of putback+get area. */ char* _IO_write_base; /* Start of put area. */ char* _IO_write_ptr; /* Current put pointer. */ char* _IO_write_end; /* End of put area. */ char* _IO_buf_base; /* Start of reserve area. */ char* _IO_buf_end; /* End of reserve area. */ /* The following fields are used to support backing up and undo. */ char *_IO_save_base; /* Pointer to start of non-current get area. */ char *_IO_backup_base; /* Pointer to first valid character of backup area */ char *_IO_save_end; /* Pointer to end of non-current get area. */ struct _IO_marker *_markers; struct _IO_FILE *_chain; int _fileno; #if 0 int _blksize; #else int _flags2; #endif _IO_off_t _old_offset; /* This used to be _offset but it's too small. */ #define __HAVE_COLUMN /* temporary */ /* 1+column number of pbase(); 0 is unknown. */ unsigned short _cur_column; signed char _vtable_offset; char _shortbuf[1]; /* char* _save_gptr; char* _save_egptr; */ _IO_lock_t *_lock; #ifdef _IO_USE_OLD_IO_FILE };
|
具体方法是:
#include <stdio.h>
#include <unistd.h>
typedef unsigned char byte;
typedef char int8;
int8 read_exact(byte* buf, int8 len);
int8 write_exact(byte* buf, int8 len);
int main() {
FILE * fp;
fp = fopen("ports.log", "a+");
fprintf(fp, "start...\n");
fflush(fp);
byte buf[256]={0};
int i = 0;
for(;;)
{
int8 len = buf[0];
//方法2:利用feof时,置标志位_flags:true表示stdin关闭;_flags:false表示stdin打开;
read(0, buf, 3);
if(stdin->_flags)
{
fprintf(fp, "stdin closed by erlang\n");
return (i);
}
write(1, buf, 3);
}
为了验证性能:此时if(stdin->_flags)并不是一直在不停的判断,只是在port_close关闭stdin,对其置位时执行。此结论是通过反推得出的,具体实验如下:
将判断改为其反面,即stdin不关闭时写文件if(!stdin->_flags) fprintf(fp, "stdin closed by erlang\n");替换掉上面的if(stdin->_flags){fprintf(fp, "stdin closed by erlang\n");
return (i);}部分,结果发现在端口打开正常工作时,并没有发生写文件的动作;当改成if(stdin->_flags) fprintf(fp, "stdin closed by erlang\n");即置位后不退出,这样
当erlang关闭端口时_flags置位了,由c进程没有退出,此条件成立,故发生了循环写文件动作。结论:c进程对stdin的动作判断与erlang进程紧密相关,并不是单方面的情况,具体的执行时隙问题,留待以后进一步分析。
附:erl的测试程序如下:
-module(echo).
-export([start/0, stop/0, echo/1]).
start() ->
spawn(fun() ->
register(echo, self()),
process_flag(trap_exit, true),
Port = open_port({spawn, "./echo"}, []),
loop(Port)
end).
stop() ->
echo ! stop.
echo(Msg) ->
echo ! {call, self(), Msg}, %% Msg必须是一个List
receive
Result -> Result
after 1000 -> io:format("time out~n"), true
end.
loop(Port) ->
receive
{call, Caller, Msg} ->
Port ! {self(), {command, Msg}}, %% Msg必须是一个List
receive
{Port, {data, Data}} -> %% 返回的Data也是一个List
Caller ! Data
end,
loop(Port);
stop ->
Port ! {self(), close},
receive
{Port, closed} -> exit(normal)
end;
{'EXIT', Port, Reason} ->
io:format("~p terminated !~n",[Port]),
exit({port_terminated, Reason})
end.