范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

skynet源码阅读系列04skynetstart收尾

  上一小节主要是梳理了一下  skynet_context_new()   的功能。
  skynet_context_new()   主要功能是创建一个 skynet_context  。
  根据传入的name 从 配置的 cpath 路径对应的  *.so   库中查找。
  我们写的 C 服务需要包含四个函数, 假如 name 为  logger  , 则这四个函数的函数名分别为logger_create logger_release logger_init logger_signal
  skynet_context   结构体包含以下成员:struct skynet_context {     void * instance;                // 创建出的服务实例     struct skynet_module * mod;     // 内存里该C服务句柄     void * cb_ud;                        skynet_cb cb;     struct message_queue *queue;    // 该服务的消息队列     ATOM_POINTER logfile;     uint64_t cpu_cost;  // in microsec     uint64_t cpu_start; // in microsec     char result[32];                // 暂存结果     uint32_t handle;                // 启动该服务后,服务号     int session_id;     ATOM_INT ref;     int message_count;                  bool init;                      // init 之后为false     bool endless;                        bool profile;                   // 配置文件 profile 的值      CHECKCALLING_DECL };
  上述成员变量没注释的从main看到这里,暂时不清楚是做什么的,往后继续阅读就清楚了。
  今天我们接着  skynet_start()   继续看, 完整的函数体在下面。void  skynet_start(struct skynet_config * config) {     // register SIGHUP for log file reopen     struct sigaction sa;     sa.sa_handler = &handle_hup;     sa.sa_flags = SA_RESTART;     sigfillset(&sa.sa_mask);     sigaction(SIGHUP, &sa, NULL);      if (config->daemon) {         if (daemon_init(config->daemon)) {             exit(1);         }     }     skynet_harbor_init(config->harbor);     skynet_handle_init(config->harbor);     skynet_mq_init();     skynet_module_init(config->module_path);     skynet_timer_init();     skynet_socket_init();     skynet_profile_enable(config->profile);      struct skynet_context *ctx = skynet_context_new(config->logservice, config->logger);     if (ctx == NULL) {         fprintf(stderr, "Can"t launch %s service ", config->logservice);         exit(1);     }      skynet_handle_namehandle(skynet_context_handle(ctx), "logger");      bootstrap(ctx, config->bootstrap);      start(config->thread);      // harbor_exit may call socket send, so it should exit before socket_free     skynet_harbor_exit();     skynet_socket_free();     if (config->daemon) {         daemon_exit(config->daemon);     } }
  只剩下下面这点没看了。 skynet_start(struct skynet_config * config) {     // ...     skynet_handle_namehandle(skynet_context_handle(ctx), "logger");      bootstrap(ctx, config->bootstrap);      start(config->thread);      // harbor_exit may call socket send, so it should exit before socket_free     skynet_harbor_exit();     skynet_socket_free();     if (config->daemon) {         daemon_exit(config->daemon);     } }
  skynet_handle_namehandle(skynet_context_handle(ctx), "logger"); const char *  skynet_handle_namehandle(uint32_t handle, const char *name) {     rwlock_wlock(&H->lock);      const char * ret = _insert_name(H, name, handle);      rwlock_wunlock(&H->lock);      return ret; }
  我们可以看到, skynet_handle_namehandle()   的功能是 _insert_name()  。
  看看  _insert_name()   的具体实现:static const char * _insert_name(struct handle_storage *s, const char * name, uint32_t handle) {     int begin = 0;     int end = s->name_count - 1;     while (begin<=end) {         int mid = (begin+end)/2;         struct handle_name *n = &s->name[mid];         int c = strcmp(n->name, name);         if (c==0) {             return NULL;         }         if (c<0) {             begin = mid + 1;         } else {             end = mid - 1;         }     }     char * result = skynet_strdup(name);      _insert_name_before(s, result, handle, begin);      return result; }
  很明显的一个二分查找,判断传入的 name 是否在  H->name  里, 这是一个指针,指向一块内存,其内存元素的类型为 skynet_name  , 定义如下:struct handle_name {     char * name;     uint32_t handle; };  struct handle_storage {     //...     int name_cap;       // *name 指向内存块的容量     int name_count;     // *name 指向内存块当前已经占用了几个元素,每个元素为一个 skynet_name     struct handle_name *name;   // 指向一块内存 };
  为什么这里可以使用二分查找?
  我们看下  strcmp()   函数的功能
  百度百科:strcmp函数是string compare(字符串比较)的缩写,用于比较两个字符串并根据比较结果返回整数。基本形式为strcmp(str1,str2),若str1=str2,则返回零;若str1str2,则返回正数。
  我们可以依据返回的结果判断,如果为0说明找到了,直接返回,后面的逻辑就不跑了,如果 <0  ,说明结果在mid的右边,begin = mid + 1  , 反之 end = mid - 1  。
  既然可以使用了二分查找,那一定要保证数组的有序咯?
  我们接着往下看,
  char * result = skynet_strdup(name);   将 name 拷贝了一份, 为什么要拷贝,下面解释。
  _insert_name_before(s, result, handle, begin);   函数原型如下:static void _insert_name_before(struct handle_storage *s, char *name, uint32_t handle, int before) {     if (s->name_count >= s->name_cap) {         s->name_cap *= 2;         assert(s->name_cap <= MAX_SLOT_SIZE);         struct handle_name * n = skynet_malloc(s->name_cap * sizeof(struct handle_name));         int i;         for (i=0;iname[i];         }         for (i=before;iname_count;i++) {             n[i+1] = s->name[i];         }         skynet_free(s->name);         s->name = n;     } else {         int i;         for (i=s->name_count;i>before;i--) {             s->name[i] = s->name[i-1];         }     }     s->name[before].name = name;     s->name[before].handle = handle;     s->name_count ++; }
  首先我们看  else   分支,此时 H->name   指向的内存还没放满元素,将从 before 下标开始到之后一个元素,将数组统一向后挪一个位置,不过都是从末端开始,到before 截至。
  before 也就是传入函数的begin, 是 name 将要插入的位置,将下标比begin 大的元素往后移,将 name 插入begin的位置,从而保证了数组的有序。
  如果  if (s->name_count >= s->name_cap)   如果容量不够了,将容量扩充至原来的两倍,并且有一个 MAX_SLOT_SIZE  最大值内存上限,将 before 之前的元素重新写入内存,之后的元素向后挪一个位置,同时释放掉原内存空间,中间 before 的位置写入 name。
  注意这里的释放操作,也就是为什么  _insert_name()  里为什么要拷贝一份的原因了。
  接下来是  bootstrap(ctx, config->bootstrap); static void bootstrap(struct skynet_context * logger, const char * cmdline) {     int sz = strlen(cmdline);     char name[sz+1];     char args[sz+1];     int arg_pos;     sscanf(cmdline, "%s", name);     arg_pos = strlen(name);     if (arg_pos < sz) {         while(cmdline[arg_pos] == " ") {             arg_pos++;         }         strncpy(args, cmdline + arg_pos, sz);     } else {         args[0] = "";     }     struct skynet_context *ctx = skynet_context_new(name, args);     if (ctx == NULL) {         skynet_error(NULL, "Bootstrap error : %s ", cmdline);         skynet_context_dispatchall(logger);         exit(1);     } }
  我们拿  example/config   下的配置举例,config 下的 bootstrap 配置如下。bootstrap = "snlua bootstrap"   -- The service for bootstrap
  bootstrap()   函数体内在调用 skynet_context_new()   之前,主要是把 "snlua" 赋值给 name,而空格之后的字符串即为args的值。
  然后再把 name,args传入 skynet_context_new() 函数,从 snlua.so 中查找模块并创建实例。
  如果失败了,把消息队列里的消息分发完,退出。
  start(config->thread); static void start(int thread) {     pthread_t pid[thread+3];      struct monitor *m = skynet_malloc(sizeof(*m));     memset(m, 0, sizeof(*m));     m->count = thread;     m->sleep = 0;      m->m = skynet_malloc(thread * sizeof(struct skynet_monitor *));     int i;     for (i=0;im[i] = skynet_monitor_new();     }     if (pthread_mutex_init(&m->mutex, NULL)) {         fprintf(stderr, "Init mutex error");         exit(1);     }     if (pthread_cond_init(&m->cond, NULL)) {         fprintf(stderr, "Init cond error");         exit(1);     }      create_thread(&pid[0], thread_monitor, m);     create_thread(&pid[1], thread_timer, m);     create_thread(&pid[2], thread_socket, m);      static int weight[] = {          -1, -1, -1, -1, 0, 0, 0, 0,         1, 1, 1, 1, 1, 1, 1, 1,          2, 2, 2, 2, 2, 2, 2, 2,          3, 3, 3, 3, 3, 3, 3, 3, };     struct worker_parm wp[thread];     for (i=0;ithread + 3  。struct monitor {     int count;     struct skynet_monitor ** m;     pthread_cond_t cond;     pthread_mutex_t mutex;     int sleep;     int quit; };
  创建了一个 monitor 结构体, 初始化数据 count: config->thread sleep: 0 m: 为每个thread 创建了一个 skynet_monitor cond: 条件变量 mutex: 互斥锁 create_thread(&pid[0], thread_monitor, m); create_thread(&pid[1], thread_timer, m); create_thread(&pid[2], thread_socket, m);
  创建了三个线程,monitor, timer, socket, 这就是声明数组大小为 thread + 3 的原因。
  create_thread()   的函数原型如下:static void create_thread(pthread_t *thread, void *(*start_routine) (void *), void *arg) {     if (pthread_create(thread,NULL, start_routine, arg)) {         fprintf(stderr, "Create thread failed");         exit(1);     } }
  参数 thread: 指向线程的指针 start_routine: 指向函数的指针 arg: void 指针
  创建线程,并指明线程运行函数的起始地址,也就是我们传入的函数指针指向的地址,arg 将最为参数传递给 start_routine 函数。 static int weight[] = {          -1, -1, -1, -1, 0, 0, 0, 0,         1, 1, 1, 1, 1, 1, 1, 1,          2, 2, 2, 2, 2, 2, 2, 2,          3, 3, 3, 3, 3, 3, 3, 3, }; struct worker_parm wp[thread]; for (i=0;idaemon) {     daemon_exit(config->daemon); }
  最后join 调线程之后再把 harbor,socket,等相关资源释放掉。
  skynet_start()   到这里就结束了,是不是很奇怪,是不是感觉还有很多事情没有做(如果有skynet使用经验会有这种感觉),别急,后面我们继续看。

1099元起,5000mAh22。5W超快闪充的荣耀畅玩30Plus诚意十足智能手机是人们日常生活中使用频率最高的电子产品,通讯社交购物支付等统统离不开它,因此,强续航成了刚需。刚好荣耀最近推出了一款拥有超大电池容量,大屏大内存的新机荣耀畅玩30Plus。如何WsgiServer。py搭建服务器目的搭建最简单的服务器框架(访问本地静态和动态资源)一大致思路1创建监听套件2绑定本地信息(IP和port)3使用listen改变监听状态4等待客户端(浏览器)的链接,并创建为客户protobuf多平台使用男人站直别趴下,胜者困难不低头。ProtocolBuffers是一种轻便高效的结构化数据存储格式,可用于结构化数据串行化,很适合做数据存储或RPC数据交换格式。它可用于通讯协议数据可惜了,这手机终于还是被干掉了重点回顾1。今天下载这App的网友,全都黑屏死机了2。今年最强的电子产品,可能是它3。城市上空出现UFO?网友们坐不住了最近的一波新机,还是让机哥有些眼花缭乱。虽然一直都说智能手机摩托罗拉重磅出击,搭载骁龙888Puls处理器的性能铁三角不到20002021年就快过去了,即将迎来2022年,对于今年的双12,各大手机厂商都忙着降价一波,但摩托罗拉重磅出击,搭载骁龙888Plus处理器的性能铁三角不到2000就能拿下,这无疑是今小米12Pro最全解析,外观确定,120W骁龙8Gn1,全能影像旗舰小米近日新消息不断,备受关注。早前雷总承诺小米12系列新机将会首发这颗纳米芯片,但从实际表现来看,小米错失了首发先机,被摩托罗拉截胡。不过这并不影像小米12系列的关注度。除了小米,云端争霸是场持久战本周,国际研究机构高德纳(Gartner)发布最新报告,全面评估全球顶级云厂商的整体能力。其中,阿里云拿下IaaS基础设施能力全球第一,在计算存储网络安全四项核心评比中均斩获最高分FireBird编程从入门到精通从来没有过这么一种数据库,能够像InterBaseFireBird一样富有激情。这是一种完全为程序员准备的数据库,就像瑞士军刀一样小巧方便实用。以往的数据库,不是太大太笨重(例如,中企敢自研芯片,就收回技术,高通哪来的底气?最近一两年里,由于全球缺芯的影响,我国有许许多多的企业在困境中渐渐得到发展和进步。在部分高端科技方面,大有直追世界先进水平的态势。最近,我国高端科技的成绩中最亮眼的无疑是芯片。但是万字教你如何用Python实现线性规划线性规划说明什么是线性规划?想象一下,您有一个线性方程组和不等式系统。这样的系统通常有许多可能的解决方案。线性规划是一组数学和计算工具,可让您找到该系统的特定解,该解对应于某些其他首推405km自由版比亚迪海豚购车指南在完成王朝家族的核心产品布局后,比亚迪又将目光转移到时下流行的小型纯电动车市场,推出了海洋系列的首款车型海豚。新车基于比亚迪e平台3。0打造,动力方面搭载磷酸铁锂刀片电池,造型方面
其实我是一个ITX机箱追风者210GMPGZ390I影驰1660TI这次更新的这台电脑主机是朋友憋大招过度用的,就是想要一步到位,预算又不允许的那种,所以相关配件上的选择会比较的灵活一些,以便后期可以做出相应的调整,虽说是过度之用,不过整机性能也不宿舍中保持安安静静ORICO(奥睿科)无线键鼠套装最近要回宿舍住了,在宿舍用青轴必定命不久矣,可是又想外接键鼠使用,加上也有些受不了有线键鼠线材的乱七八糟,正好ORICO(奥睿科)有一套无线键鼠套装,七十几元的售价,也比较便宜,于夏天来了你智慧用电了没?夏天即将到来,高温火力全开,火辣辣的夏季是安全事故高发的季节。用电不当高温引发火灾等事件层出不穷,如何预防各种潜在的危险是亟待解决的事情。为切实加强安全保障,预防安全事故的发生,针38女王节给媳妇整的过节礼物达宝利人体工学座椅前言在这之前本人对于这种人体工学电脑椅的并没有什么概念,本着耐用,采购的家具都是以实木为主,其中也包括了自己日常用得最多的电脑桌和电脑椅子,主要是因为结婚那会为了家具统一风格采购的德国ITT104不开机故障维修德国ITT104收音机不开机故障ITT104是上世纪70年代末的产物,属于经典的ITT103的下一代,音质一样优美,不同的是从104开始电路引入了集成电路,而非103以前的纯晶体管麻雀虽小五脏俱全ROGSTRIXCARRY双模便携游戏鼠标开箱体验作为一名ROG玩家国度的硬件爱好者,个人也是蛮关注他们家的外设产品,特别是今年发布的GladiusII无线版本,毕竟在这之前ROG貌似也就只有一款斯巴达的无线游戏鼠标来着,买过用过安卓全智能表皇!OPPOWatch2全方位升级,到手竟不到千元欲戴皇冠,必承其重。OPPO在OPPOWatch2系列发布之前,宣称它摸到了安卓智能手表天花板。OPPOWatch2在7月27日正式发布,也将于8月6日正式发售。这款号称安卓天花板蓝牙耳机推荐之前跟风入了其它牌子的蓝牙耳机后面掉了一耳之前也遇到的就是接连问题比较多!有的时候用的真的好气!!听歌的时候接个电话啥的它就断开连接了?我晕这次换了一个魔浪的耳机应该很多人都知道这谷歌Android12将禁止第三方分享App5月28日消息许多AndroidApp都支持分享功能,当用户点击分享按钮,如果没有自行实现分享功能,就会调用Android系统的分享功能。此外,市面上也有许多第三方分享App,在功2019ROG新品发布会,电竞外设天生BUFF信仰闪耀7月23日,ROG玩家国度与北京凯德拉克中心召开了新品发布会。华硕集团董事长施崇棠华硕共同执行长许先越腾讯副总裁王波京东集团副总裁姚彦中华硕电脑全球副总裁许佑嘉华硕电脑中国业务总部2019ROG新品发布会BUFF信仰,电竞显示器帧速全释放2019年7月23日,ROG玩家国度在北京凯迪拉克中心M空间召开天生BUFFROG新品发布会。华硕集团董事长施崇棠华硕共同执行长许先越腾讯副总裁王波京东集团副总裁姚彦中华硕电脑全球