Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1906482
  • 博文数量: 217
  • 博客积分: 4362
  • 博客等级: 上校
  • 技术积分: 4180
  • 用 户 组: 普通用户
  • 注册时间: 2009-09-20 09:31
文章分类

全部博文(217)

文章存档

2017年(1)

2015年(2)

2014年(2)

2013年(6)

2012年(42)

2011年(119)

2010年(28)

2009年(17)

分类: Python/Ruby

2011-10-18 17:27:14

vnc是一款开源的远程控制软件,它能完整的将窗口界面,通过网络传输到另外一台计算机的屏幕上。在linux下,vnc包括四个命令:vncserver,vncviewer,vncpasswd,vncconnect。在连接一台计算机的时候,需要知道对方计算机的IP和端口号,在我们的项目中,虚拟机的端口号是由master分配的,因此,master就要实现vncport的分配了,vncport的分配范围一般在5900到65535之间。
之前写的vncport的分配是用mnesia数据库来进行的,分配一个port之后在数据库中进行标记,也没有涉及比较复杂的算法,这种方法可以实现所要求的功能,但是在这将近65536个数据当中,每次要找一个没有使用的数据,这个时间也是比较长的。
后来,在学习进程的pid号分配的时候,也涉及到了这个问题,不过进程的pid分配范围是在0到32767之间,上面的数目是这个数目的2倍。因此,想借用内核中的pid分配算法(pid位图)来实现这里的vncport分配问题。

先来看一下pid位图算法:
为了表示32768个进程号是不是分配出去没有,我们要占用32768/8=4096个字节来标识,用每一位来标记一个pid号是不是分配出去。为此,我们可以定义一个这样的结构体:
typedef struct pidmap 
{
    unsigned int nr_free;
    char page[4096];
} pidmap_t;
用nr_free表示未分配出去的pid号的数目。
如果要根据一个给定pid号,要找它在这个结构体当中所对应的位的时候,可能我们首先想到的是下面的这个算法:
我们把上面的存放位图的变量想像成一个4096行,8列的表。首先给pid除以8,然后取整,即int a=(pid/8),将得到的a的值作为pidmap.page的下标,也就是pidmap.page[a],然后用int b=(pid%8)得到对应位的0—7之间的编号,这样的话,就唯一确定了一个pid编号在pidmap.page中的具体位置。

这个方法也是可以实现的,但是内核当中是通过移位操作来实现的。我们可以抽象出一张表,这个表有32列,1024行,这个刚好是一个页的大小。这个时候,我们可以通过pid的后5位值(变化范围在0—31之间)来确定在某一行的具体的列。通过pid的高27位(pid本身是32位的)来表示在具体的某一行。所以我们可以通过移位操作来实现。
具体的移位操作主要有两句:
unsigned long mask = 1UL << (offset & 31);
unsigned long *p = ((unsigned long*)addr) + (offset >> 5);
其中的offset表示的是一个要处理的pid号。
这样就可以实现了。
有关的详细的解释以及应用可以看我的博客:
http://blog.chinaunix.net/space.php?uid=22566367&do=blog&id=2845765

下面开始erlang中的vncport的分配。
Erlang 是面向函数式编程的,进程本身没有全局变量,在函数上下文中,可有局部变量,所有的局部变量都是readonly,并只能被赋值一次。因此,为了保存这个位图,可以借助数据库进行保存。
-record(vncport, {vnc_id, vnc_value}).
数据库弄两列,一列是vnc_id,一列是vnc_value。在这个数据库的每一项中,vnc_id表示的是vncport的一个地址,具体对应的是某一个vncport的高11位(65535是16位);而vnc_value是一个32位的数据,用来表示32个vncport。

具体程序代码是:

  1. %% File: vncport.erl
  2. %% Created: 2011-10-18
  3. %% Description:
  4. -module(vncport).
  5. -date("2011.10.18").

  6. %%
  7. %% Include files
  8. %%
  9. -include_lib("stdlib/include/qlc.hrl").

  10. %%
  11. %% Export functions
  12. %%
  13. -export([start/0, stop/0, set_bit/1, get_next_vncport/0, free_vncport/1]).

  14. -record(vncport, {vnc_id, vnc_value}).

  15. %%
  16. %% API functions
  17. %%
  18. start() ->
  19.     mnesia:stop(),
  20.     mnesia:delete_schema([node()]),
  21.     mnesia:create_schema([node()]),
  22.     mnesia:start(),
  23.     mnesia:create_table(vncport, [{attributes, record_info(fields, vncport)}]),
  24.     vncport_init(),
  25. %    mnesia:dirty_update_counter(vncport, last_used, -1),
  26.     F = fun() ->
  27.             Row = #vncport{vnc_id=last_used, vnc_value=-1},
  28.             mnesia:write(Row)
  29.         end,
  30.     mnesia:transaction(F),
  31.     io:format("mnesia start~n"),

  32.     alloc_vnc(0, 5900).

  33. stop() ->
  34.     mnesia:stop().

  35. get_next_vncport() ->
  36.     %% find last used vncport
  37.     F = fun() ->
  38.             Query = qlc:q([X#vncport.vnc_value || X <- mnesia:table(vncport), X#vncport.vnc_id =:= last_used]),
  39.             [E] = qlc:e(Query),
  40.             io:format("Last used vncport is:~p~n", [E]),
  41.             E
  42.         end,
  43.     {atomic, R} = mnesia:transaction(F),
  44.     Result = R+1,
  45.     io:format("init vncport value is ~p~n", [Result]), %% debug info
  46.     search(search, Result).

  47. free_vncport(N) ->
  48.     Mask = 1 bsl (N band 31),
  49.     Address = N bsr 5,
  50.     io:format("Mask:~p, Address:~p~n", [Mask, Address]), %% debuf info
  51.     F = fun() ->
  52.             Query = qlc:q([X#vncport.vnc_value || X <- mnesia:table(vncport), X#vncport.vnc_id =:= Address]),
  53.             [E] = qlc:e(Query),
  54.             Value = E band (bnot Mask),
  55.             New = #vncport{vnc_id=Address, vnc_value=Value},
  56.             mnesia:write(New)
  57.         end,
  58.     mnesia:transaction(F).

  59. set_bit(N) ->
  60.     Mask = 1 bsl (N band 31),
  61.     Address = N bsr 5,
  62.     io:format("set number:~p~n", [N]), %% debuf info
  63. %    io:format("Mask:~p, Address:~p~n", [Mask, Address]), %% debuf info
  64.     F = fun() ->
  65.             Query = qlc:q([X#vncport.vnc_value || X <- mnesia:table(vncport), X#vncport.vnc_id =:= Address]),
  66.             [E] = qlc:e(Query),
  67.             Value = E bor Mask,
  68.             New = #vncport{vnc_id=Address, vnc_value=Value},
  69.             mnesia:write(New),
  70.             io:format("update over :~p ~p~n~n", [Value, New]) %% debug info
  71.         end,
  72.     mnesia:transaction(F).

  73. %%
  74. %% Local Functions
  75. %%
  76. vncport_init() ->
  77.     F = fun(N) ->
  78.             init_one_vncport(N)
  79.         end,
  80.     for(0, 2047, F).

  81. init_one_vncport(N) ->
  82.     Row = #vncport{vnc_id=N, vnc_value=0},
  83.     F = fun() ->
  84.             mnesia:write(Row)
  85.         end,
  86.     mnesia:transaction(F).

  87. search(over, N) ->
  88.     io:format("vncport ~p can use~n", [N]),
  89.     ok;
  90. search(search, 65536) ->
  91.     io:format("search ~n"),
  92.     search(search, 0);
  93. search(search, Vncport) ->
  94.     io:format("search ~p~n", [Vncport]),
  95.     case is_empty(Vncport) of
  96.         true ->
  97.             set_bit(Vncport),
  98.             update_dirty(Vncport),
  99.             search(over, Vncport);
  100.         false ->
  101.             Next = Vncport+1,
  102.             search(search, Next)
  103.     end.

  104. is_empty(Vncport) ->
  105.     io:format("begin check~n"),
  106.     Mask = 1 bsl (Vncport band 31),
  107.     Address = Vncport bsr 5,
  108.     F = fun() ->
  109.             Query = qlc:q([X#vncport.vnc_value || X <- mnesia:table(vncport), X#vncport.vnc_id =:= Address]),
  110.             [E] = qlc:e(Query),
  111.             Result = E band Mask,
  112.             if
  113.                 Result =:= 0 ->
  114.                     true;
  115.                 true ->
  116.                     false
  117.             end
  118.         end,
  119.     {atomic, R} = mnesia:transaction(F),
  120.     io:format("over check, result is ~p~n", [R]),
  121.     R.

  122. update_dirty(Vncport) ->
  123.     Row = #vncport{vnc_id=last_used, vnc_value=Vncport},
  124.     F = fun() ->
  125.             mnesia:write(Row)
  126.         end,
  127.     mnesia:transaction(F).

  128. for(Max, Max, F) ->
  129.     [F(Max)];
  130. for(Min, Max, F) ->
  131.     [F(Min)|for(Min+1, Max, F)].

  132. alloc_vnc(Min, Min) ->
  133.     io:format("alloc last vnc:~p~n", [Min]),
  134.     set_bit(Min);
  135. alloc_vnc(Min, Max) ->
  136.     io:format("alloc vnc:~p~n", [Min]),
  137.     set_bit(Min),
  138.     alloc_vnc(Min+1, Max).

API接口说明:
start():初始化数据库。
stop():关闭数据库。
set_bit(N):将N这个vncport端口号置为不可用。
get_next_vncport():得到一个可以分配的vncport端口号。
free_vncport(N):释放vncport为N的这个端口号。

阅读(2332) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~