Erlang笔记

邹京
2023-12-01

进程链接:
通过调用process_flag(trap_exit,true)来把一个普通进程转为系统进程,系统进程能捕获它所链接的进程的退出消息。

进程A与进程B链接,进程B调用process_flag(trap_exit,true),进程B为系统进程,进程A正常常退出时,进程B捕获到并处理A退出的消息,进程B继续正常运行。
进程A与进程B链接,进程B调用process_flag(trap_exit,true),进程B为系统进程,进程A异常退出时,进程B捕获到并处理A退出的消息,进程B继续正常运行。


进程A与进程B链接,进程B调用process_flag(trap_exit,false),进程B不是系统进程,进程A正常退出时,进程B不会捕获到A的退出消息,进程B继续正常运行。
进程A与进程B链接,进程B调用process_flag(trap_exit,false),进程B不是系统进程,进程A异常退出时,进程B不会捕获到A的退出消息,进程B也退出了。

ets:
如果已经定义了一个记录:-record(player,{id,name,age}).
如果再定义一个ets用于保存玩家信息:ets:new(player,[set,protected,named_table,{keypos,#player.id}]).
编译文件的时候没问题,程序运行的时候在新建ets表player的时候会报错(** exception exit: badarg in function  ets:new/2)。
解决方法:表名和记录名冲突了,随便改一个。

pid:
{ok,Pid} = gen_server:start_link(Name,Module,[],[]).调用此句的时候,会把Pid注册到名字Name,所以不能再次注册register(otherName,Pid).
whereis(name)的参数是进程注册的名字,返回的是Pid。
is_pid(P).返回的是true/false.
给pid发送消息不会有任何错误,哪怕该进程不存在;但是,当注册的进程不存在时,通过注册名给它发消息会出错。 ->
不管pid是否存在都可以用pid ! {data}来发送数据,但是注册register(otherName,Pid).后,otherName !{data}这样发消息的时候,要确保注册名存在(改为whereis(Name) ! {data} 这样发送?),否则会报错。

gen_server:
handle_call/3函数里面不能再次调用回调模块是本模块的gen_server:call/2函数,否则会造成死锁。
加了-behavior(gen_server)声明后,即使定义了六个回调函数,仍然会产生警告说没有定义,
解决方法,还要加上:     -export([init/1,handle_call/3,handle_cast/2,handle_info/2,terminate/2,code_change/3]).

gen_server:start_link的调用会生成一个服务器进程且连接到进程树,并调用我们的init函数。 gen_server:call(ch3, alloc)的调用导致对handle_call的调用,这是同步的。gen_server:cast(ch3, {free, Ch})的调用导致对handle_cast的调用,这是异步的。很简单。
 
假如你不想把服务器进程挂入监控树的话,直接用gen_server:start启动进程,这是这个服务器进程就是一个普通进程了。
 
gen_server的停止规则:
 1.以gen_server:start_link开始的连入监控树的
 
一般情况不需要提供自己的停止函数,监控进程会自动处理,但是如果你想在gen_server进程中自己清理以下资源,那么就必须在init函数里调用process_flag(trap_exit, true)来捕获退出信号,这会导致调用terminate(shutdown, State)函数,所以你也必须实现这个函数
 2.以gen_server:start开始的单独gen_server
 
终止他就比较简单,直接调用gen_server:cast(Name, stop),这会导致调用handle_cast(stop, State),它的实现里写入 {stop, normal, State}即可,它最终导致terminate(normal, State)的调用,你的清理工作就可以在这继续了。


关于gen_tcp:controlling_process:
Result = gen_tcp:controlling_process(Socket,self()),返回{error,not_owner}.只能在socket当前控制进程下指定新的控制进程?
参考:Assigns a new controlling process Pid to Socket.
The controlling process is the process which receives messages from the socket.
If called by any other process than the current controlling process, {error, not_owner} is returned.参考帮助手册原文:http://www.erlang.org/doc/man/gen_tcp.html
总结:gen_tcp:controlling_process(Socket,Pid)必须在Socket的当前控制进程调用。
      而且,当Socket的打开类型为被动类型,即以{active,false}模式打开套接字,此时,Socket的控制进程只能用gen_server:recv(Socket,N)来接收套接字发来的消息。
      若想套接字控制进程能以receive的方式来接收套接字消息,则可以把套接字设为主动或半阻塞模式,即{acitve,true}或{active,once}。
      如果是{active,once}模式,那么控制进程在接收一个消息后,必须显式地调用函数inet:setopts(Socket,[{active,once}])来把套接字激活,以便接收下一个消息,如不激活,系统会处于阻塞状态。
      (参考<<erlanag程序设计>>14章套接字编程14.2节)

 

20140425
监控树:
添加监控树,启动监控树进程,监控策略里面所配置的启动进程也会跟着启动,需要注意进程重复启动带来的问题。
ETS:
进程创建一个ETS表,则该进程为ETS表的所有者,正常情况,ETS表会随着所有者进程的销毁而销毁。对ETS表的所有权可以转让,
两种方法转让:

1.在该ETS表的当前所有者进程里面,通过调用give_away(TableName,Pid,GiftData)主动转让ETS表的所有权给Pid指向的进程。
2.在创建表的时候,加上heir选项,如:ets:new(TableName,[protected,ordered_set,named_table,{heir,Pid,Data},{keypos,#recordName.keyWord}]).
                这种情况会在当前ETS表的所有者进程销毁的时候转让所有权给继承者Pid。
创建ETS表的时候,表名name只跟nameed_table属性设置与否有关:
如:ets:new(TableName,[protected,ordered_set,{heir,Pid,Data},{keypos,#recordName.keyword}]),创建表的时候不加named_table字段,则可以多次调用该new语句创建不同的表,返回的表tableId会不一样。
假如创建ETS表的时候,加了named_table属性,则不能多次创建同名的表,会报bad argument错误。

 

 

 

 类似资料: