目录

scheduler介绍

scheduler调度的event与job的关系

scheduler的主要操作

job又是如何创建的呢?

job的执行


scheduler介绍

scheduler使用heap(最小堆)数据结构存储events。event结构体只要两个成员:time和job。最小堆就是根节点的time for fire the event值最小,即根节点将被第一个执行。

scheduler队列给事件设置了时间,随后传给processor。

scheduler是通过堆来实现的。堆是一种特殊的树状数据结构满足以下属性:
如果B是A的子节点,那么key(A)>=或者<=key(B)。也就是最大的或者最小的key的元素就是堆的根节点(最大堆或最小堆)。
我们使用最小堆的key作为一个事件被执行的unix绝对时间。所以根节点总是要被执行事件的下一个,每次都是根节点被执行。

scheduler的早期实现使用一个有序链表来存储事件。优点是删除下一个事件是相当快的,添加一个事件也是相当快的。问题是添加一个事件在任意节点之间会变慢,特别是事件的数量增长特别快的时候。对于每个连接都会有几个事件:IKE-rekey,NAT-keepalive,retransmissions,expire(半打开)等等。所以一个网关可能不得不处理上千个并发的连接,有不得不尽快处理大量事件的队列。

锁机制使情况变的更糟,为了保护线程安全,当有事件要入队列的时候,没有事件可以被处理,所以使插入动作变快就显得非常重要。

那就是堆树的优势了。添加一个元素到堆树的时间复杂度为O(log n),换句话说,删除根节点也需要O(log n)的时间复杂度。以1000个事件为例,插入一个新的事件到链表里需要1000次比较,在堆树的实现里,最糟糕的情况也就13.3次比较,这是显著地提高。实现的原理是用一个二叉树映射到一维数组上来存储元素。这样减少了存储开销并简化了遍历。位置n节点的子节点放在了位置2n和位置2n+1处(同样位置n节点的父节点在位置[n2]处)。因而上下遍历这棵树被简化为简单索引值的计算。

向堆树添加一个新节点的流程是这样的:
堆树总是从左向右塞,直到一排塞满了,开始往下一排塞。映射到一个数组就像新的节点放在数组第一个空位置那样简单。在一维数组里的位置数就等于在堆树里节点的个数。随后堆树开始调整直到满足,比如这个新的节点不得不在树里冒泡排序直到父亲节点的值更小或者这个节点变成了树的新的根节点。

从堆树中删除下一个节点流程更简单:

事件本身是根节点或者存储在数组最前面的节点。在删除了它后,根节点不得不被替代,堆树又要做调整。这件事是通过移动最低端的节点(最后一排,最右边的节点)到根节点位置,并将它与它的叶子节点交换做向下调整直到它的子节点没有比它更小的值或者它就是子节点了。

原文描述请见:libstrongswan/processing/scheduler.h

关于堆树heap的数据结构操作可参考用数组实现堆的描述。

scheduler调度的event与job的关系

如以下代码清晰可见,event就是标上time的job。

/*** Event containing a job and a schedule time*/
struct event_t {/*** Time to fire the event.*/timeval_t time;/*** Every event has its assigned job.*/job_t *job;
};/* file : src/libstrongswan/processing/scheduler.c */

scheduler的主要操作

所以scheduler只是调度关系,即把要执行的事件要入堆树,在堆树内按事件的时间来调整,让事件时间值最小的节点为根节点,因为根节点总是最先执行,就保证了事件按时间大小顺序执行。

事件就是标上时间后的job,通过以下三个函数被插入heap。

lib->scheduler->schedule_job,
lib->scheduler->schedule_job_ms,
lib->scheduler->schedule_job_tv

这三个函数是把job带上time生成event塞进heap。

job又是如何创建的呢?

比如IKEv2重传消息,先用retransmit_job_create创建job,然后调用schedule_job_ms把与job绑定的event塞入heap。

job = (job_t*)retransmit_job_create(this->initiating.mid,this->ike_sa->get_id(this->ike_sa));
lib->scheduler->schedule_job_ms(lib->scheduler, job, timeout);/* file: libcharon/sa/ikev2/task_manager_v2.c */

src/libcharon/processing/jobs/ 目录下有很多jobs的创建实现

strongswan/src/libcharon/processing/jobs$ ls *.c *.h
acquire_job.c          dpd_timeout_job.h         migrate_job.c          rekey_ike_sa_job.h    send_keepalive_job.c
acquire_job.h          inactivity_job.c          migrate_job.h          retransmit_job.c      send_keepalive_job.h
adopt_children_job.c   inactivity_job.h          process_message_job.c  retransmit_job.h      start_action_job.c
adopt_children_job.h   initiate_mediation_job.c  process_message_job.h  retry_initiate_job.c  start_action_job.h
delete_child_sa_job.c  initiate_mediation_job.h  redirect_job.c         retry_initiate_job.h  update_sa_job.c
delete_child_sa_job.h  initiate_tasks_job.c      redirect_job.h         roam_job.c            update_sa_job.h
delete_ike_sa_job.c    initiate_tasks_job.h      rekey_child_sa_job.c   roam_job.h
delete_ike_sa_job.h    mediation_job.c           rekey_child_sa_job.h   send_dpd_job.c
dpd_timeout_job.c      mediation_job.h           rekey_ike_sa_job.c     send_dpd_job.h

job的执行

libstrongswan/processing/scheduler.c 里schedule函数 就是把event从heap中取出来(peak_event,即根节点:heap[1])并调用lib->processor->queue_job(lib->processor, event->job);把event关联的job传给processer。

schedule()函数末尾return JOB_REQUEUE_DIRECT;也就是一直重复执行,即一直从heap中取event传给processor处理。

process_create()会创建线程池并用set_threads()函数处理jobs。

set_threads()函数内会创建线程来执行process_jobs回调函数。

worker->thread = thread_create(process_jobs, worker);
if (worker->thread)
{this->threads->insert_last(this->threads, worker);this->total_threads++;
}/* file: libstrongswan/processing/processor.c */

scheduler与event的关系:

struct private_scheduler_t {/*** Public part of a scheduler_t object.*/scheduler_t public;/*** The heap in which the events are stored.*/event_t **heap;/*** The size of the heap.*/u_int heap_size;/*** The number of scheduled events.*/u_int event_count;/*** Exclusive access to list*/mutex_t *mutex;/*** Condvar to wait for next job.*/condvar_t *condvar;
};/* file: src/libstrongswan/processing/scheduler.c */

watcher与job之间的关系

struct private_watcher_t {/*** Public watcher_t interface.*/watcher_t public;/*** List of registered FDs*/entry_t *fds;/*** Last registered FD*/entry_t *last;/*** Number of registered FDs*/u_int count;/*** Pending update of FD list?*/bool pending;/*** Running state of watcher*/watcher_state_t state;/*** Lock to access FD list*/mutex_t *mutex;/*** Condvar to signal completion of callback*/condvar_t *condvar;/*** Notification pipe to signal watcher thread*/int notify[2];/*** List of callback jobs to process by watcher thread, as job_t*/linked_list_t *jobs;
};/* file: src/libstrongswan/processing/watcher.c */

processor与threads,work_threads和jobs之间的关系:

struct private_processor_t {/*** Public processor_t interface.*/processor_t public;/*** Number of running threads*/u_int total_threads;/*** Desired number of threads*/u_int desired_threads;/*** Number of threads currently working, for each priority*/u_int working_threads[JOB_PRIO_MAX];/*** All threads managed in the pool (including threads that have been* canceled, this allows to join them later), as worker_thread_t*/linked_list_t *threads;/*** A list of queued jobs for each priority*/linked_list_t *jobs[JOB_PRIO_MAX];......
};/* file: src/libstrongswan/processing/processor.c */

processor、thread和jobs之间的关系

/*** Worker thread*/
typedef struct {/*** Reference to the processor*/private_processor_t *processor;/*** The actual thread*/thread_t *thread;/*** Job currently being executed by this worker thread*/job_t *job;/*** Priority of the current job*/job_priority_t priority;} worker_thread_t;/* file: src/libstrongswan/processing/processor.c */

在调用queue_job函数时,输入参数为JOB_PRIO_CRITICAL的都为轮巡线程,因为job执行结束时会return JOB_REQUEUE_DIRECT;也就是继续入队列执行。

$ grep JOB_PRIO_CRITICAL . -r --exclude=*.{o,so}
./libstrongswan/processing/scheduler.c:     NULL, return_false, JOB_PRIO_CRITICAL);
./libstrongswan/processing/watcher.c:       JOB_PRIO_CRITICAL));
./libstrongswan/processing/watcher.c:       NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
./libstrongswan/plugins/pkcs11/pkcs11_manager.c:  entry, NULL, cancel_events, JOB_PRIO_CRITICAL));
./libstrongswan/plugins/keychain/keychain_creds.c:  this, NULL, (void*)cancel_monitor, JOB_PRIO_CRITICAL));
./libcharon/network/receiver.c:         this, NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
./libcharon/network/sender.c:           this, NULL, (callback_job_cancel_t)return_false, JOB_PRIO_CRITICAL));
./charon-nm/nm/nm_backend.c:            NULL, (callback_job_cancel_t)cancel, JOB_PRIO_CRITICAL));
./libpttls/pt_tls_dispatcher.c:         JOB_PRIO_CRITICAL));

strongswan libstrongswan scheduler,processor,job,event和thread之间的关系相关推荐

  1. Message、Handler、Message Queue、Looper、Thread之间的关系(未完成)

    1. 请解释下在单线程模型中Message.Handler.Message Queue.Looper.Thread之间的关系 2. 什么是IntentService?有何优点? 1) 它是对单线程消息 ...

  2. 理清ThreadLocal、ThreadLocalMap、Thread之间的关系

    引言 这三种的关系由于大量的内部类的关系,第一次看的时候还是有点绕的,感觉你是老子的孙子,又是老子的老子.我还是建议你先抛开内部类的关系,把每一个类当作普通类来看到,理解每個类的职责,最后再把内部类放 ...

  3. 多线程并发可能遇到的问题及Runable和Thread之间的关系

    一.多线程并发可能遇到的问题 多线程并发执行可能会导致一些问题: 安全性问题:在单线程系统上正常运行的代码,在多线程环境中可能会出现意料之外的结果. 活跃性问题:不正确的加锁.解锁方式可能会导致死锁或 ...

  4. Activity与Thread之间的通讯(old)

    在Android中要让Activity与Thread进行通讯 其实很简单. 重点就在于android .os.Handler.java.lang.Thread以及android.os.Message这 ...

  5. 组件设计实战--组件之间的关系 (Event、依赖倒置、Bridge)

        一个组件与另一个组件之间的关系可以通过三种方式建立起来:事件.依赖倒置.Bridge.现在我们只考虑单向依赖的关系,即信息提供者和信息消费者.事件是一种松耦合的信息发布方式,事件发布者(信息提 ...

  6. Android Handler Runnable和Thread之间的区别和联系详解

    在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口:Thread类是在java.lang包中定义的.一个类只要继承了Thread类同时覆写了本类中的run() ...

  7. Android开发:Handler Runnable和Thread之间的区别和联系 应用--------------------看完本篇,从此一览无余!...

    http://blog.csdn.net/yanzi1225627/article/details/8582081 在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnab ...

  8. cpu、socket、core、thread 等术语之间的关系

    当我们在看技术文档时,经常会发现很多有关 cpu 的术语,比如 cpu.cpu socket.cpu core.hyper-threading 等,乱乱的分不清楚,这篇文章我带大家用三分钟时间,快速的 ...

  9. thread和threadLocal之间的关系

    2019独角兽企业重金招聘Python工程师标准>>> 是现有Thread呢还是现有ThreadLocal呢? 个人觉得应该是先有Thread的.Thread包括了很多属性,而这些值 ...

最新文章

  1. python第三方库安装-多种方式
  2. Spring - shortcuts
  3. html dd自动换行,为什么我的dd里面的内容没有自动换行呢
  4. 类的实例属性和类属性的区别
  5. kmean法和dbscan法的直观比较
  6. 浅析数据链路层的介质访问控制
  7. HTML5 data-* 自定义属性 ---转载 原文地址:https://www.cnblogs.com/dolphinX/p/3348458.html...
  8. 【python第三方库】playwright简要入门
  9. 微信公众号,带参二维码/推广二维码的使用
  10. 自定义gerrit提交脚本
  11. 15051:小Biu的区间和
  12. Oracle转MySQL存储函数percentile_cont(比例) WITHIN GROUP( ORDER BY to_number(分数) )用法
  13. unknown类型的使用
  14. 知识图谱与推荐系统综述
  15. Hadoop-HDFS
  16. HACK RF学习之旅记录3——安装WIN10+Ubuntu双系统
  17. 建功立业的秘诀就是:立即行动!
  18. JavaScript ES6函数:优点
  19. 4、某手游app登陆算法分析【Android逆向分析学习】
  20. Google Chrome 66 稳定版更新:修复四大严重安全漏洞

热门文章

  1. Dialog DA14531 DSPS 快速、重复的断连设备导致的死机不广播问题解决办法
  2. “狂飙”的自动驾驶车,用了哪些规控算法?
  3. 磁盘虚拟化系列(一):qcow2文件、raw文件、qcow2镜像、vmdk文件
  4. 速达数据库服务器密码修改,如何创建SQL数据库登录用户及密码? 找昆明速达软件...
  5. [FROM WOJ]#4376 种树
  6. (毕业设计资料)基于51单片机太阳能风能路灯电路系统设计
  7. 使用rpm -q mysql 出现 package mysql is not installed
  8. 【​观察】探访中国首家高端装备智能工厂 浪潮重新定义计算新未来
  9. 三进制状压动态规划【涂抹果酱】 题解
  10. 视频直播源码,插入图片、删除图片、设置图片大小、提取图片