關於Yarn原始碼那些事-前傳之ResourceManager篇(一)初始化
在關於Yarn那些事的部落格裡,介紹的主要是針對任務提交的一個動態流程說明,而其中牽涉到的一些細節問題,必須通過Resourcemanager的啟動和NodeManager的啟動,來更好的說明。
而本系列,就詳細說說ResourceManager啟動過程中,都發生了什麼。
我們都知道,Yarn的啟動指令碼是start-yan.sh,我們就從這個指令碼開始,琢磨琢磨。
"$bin"/yarn-daemon.sh --config $YARN_CONF_DIR start resourcemanager
腳本里這句話,指向了本目錄下的yarn-daemon.sh指令碼,命令引數指定了resourcemanager,接著看yarn-daemon.sh指令碼:
nohup nice -n $YARN_NICENESS "$HADOOP_YARN_HOME"/bin/yarn --config $YARN_CONF_DIR $command "[email protected]" > "$log" 2>&1 < /dev/null &
這句話很關鍵,交代了我們實際的啟動指令碼是bin目錄下的yarn,我們看下:
elif [ "$COMMAND" = "resourcemanager" ] ; then CLASSPATH=${CLASSPATH}:$YARN_CONF_DIR/rm-config/log4j.properties CLASSPATH=${CLASSPATH}:"$HADOOP_YARN_HOME/$YARN_DIR/timelineservice/*" CLASSPATH=${CLASSPATH}:"$HADOOP_YARN_HOME/$YARN_DIR/timelineservice/lib/*" CLASS='org.apache.hadoop.yarn.server.resourcemanager.ResourceManager' YARN_OPTS="$YARN_OPTS $YARN_RESOURCEMANAGER_OPTS" if [ "$YARN_RESOURCEMANAGER_HEAPSIZE" != "" ]; then JAVA_HEAP_MAX="-Xmx""$YARN_RESOURCEMANAGER_HEAPSIZE""m" fi
終於順利找到了根基,原來根據我們指定的指令碼,找到的是ResourceManager這個類來啟動的,下面就看看這個類。
先來看下注釋:
/** * The ResourceManager is the main class that is a set of components. "I am the * ResourceManager. All your resources belong to us..." * */ @SuppressWarnings("unchecked") public class ResourceManager extends CompositeService implements Recoverable
格外霸氣,管理整個叢集內所有的資源,並且繼承了CompositeService類,這是一個服務類,不多介紹了,主要提供了一些服務初始化和啟動的方法,供子類使用。
從ResourceManager的成員變數開始看起:
protected ClientToAMTokenSecretManagerInRM clientToAMSecretManager = new ClientToAMTokenSecretManagerInRM();
protected RMContainerTokenSecretManager containerTokenSecretManager;
protected NMTokenSecretManagerInRM nmTokenSecretManager;
protected AMRMTokenSecretManager amRmTokenSecretManager;
private Dispatcher rmDispatcher;
protected ResourceScheduler scheduler;
private ClientRMService clientRM;
protected ApplicationMasterService masterService;
private ApplicationMasterLauncher applicationMasterLauncher;
private AdminService adminService;
private ContainerAllocationExpirer containerAllocationExpirer;
protected NMLivelinessMonitor nmLivelinessMonitor;
protected NodesListManager nodesListManager;
private EventHandler<SchedulerEvent> schedulerDispatcher;
protected RMAppManager rmAppManager;
protected ApplicationACLsManager applicationACLsManager;
protected QueueACLsManager queueACLsManager;
protected RMDelegationTokenSecretManager rmDTSecretManager;
private DelegationTokenRenewer delegationTokenRenewer;
private WebApp webApp;
protected RMContext rmContext;
protected ResourceTrackerService resourceTracker;
private boolean recoveryEnabled;
很多,具體可以參照每個類的用法,在此不多說,其中牽涉到Application較多的,比如RMAppManager,RMContext等,需要細看,這是廢話,每個都值得研究。
看Main方法:
Configuration conf = new YarnConfiguration();
ResourceManager resourceManager = new ResourceManager();
ShutdownHookManager.get().addShutdownHook(new CompositeServiceShutdownHook(resourceManager),
SHUTDOWN_HOOK_PRIORITY);
setHttpPolicy(conf);
resourceManager.init(conf);
resourceManager.start();
直接把目光聚焦在這裡,我們重點研究下服務的初始化和啟動。
this.rmDispatcher = createDispatcher();
addIfService(this.rmDispatcher);
初始化的第一個關鍵點,建立排程器,這是ResourceManager非同步排程的關鍵,看看這個方法:很簡單:
protected Dispatcher createDispatcher() {
return new AsyncDispatcher();
}
很明顯,這是個非同步排程器,看看這個類的註釋和初始化步驟:
/**
* Dispatches {@link Event}s in a separate thread. Currently only single thread
* does that. Potentially there could be multiple channels for each event type
* class and a thread pool can be used to dispatch the events.
*/
@SuppressWarnings("rawtypes")
@Public
@Evolving
public class AsyncDispatcher extends AbstractService implements Dispatcher
這也是一個需要啟動的服務,用於事件的排程:
public AsyncDispatcher() {
this(new LinkedBlockingQueue<Event>());
}
public AsyncDispatcher(BlockingQueue<Event> eventQueue) {
super("Dispatcher");
this.eventQueue = eventQueue;
this.eventDispatchers = new HashMap<Class<? extends Enum>, EventHandler>();
}
注意,Dispatcher內部封裝了一個阻塞佇列,執行過程中會把事件都放在這個池子裡,並進行排程處理,同時定義了一個eventDispatchers,後續程式碼更容易看懂這個map的作用:
我們仔細看看AsyncDispatcher的服務初始化程式碼:
@Override
protected void serviceInit(Configuration conf) throws Exception {
this.exitOnDispatchException = conf.getBoolean(Dispatcher.DISPATCHER_EXIT_ON_ERROR_KEY,
Dispatcher.DEFAULT_DISPATCHER_EXIT_ON_ERROR);
super.serviceInit(conf);
}
目前來說,AsyncDispatcher的初始化程式碼先到這兒,我們繼續看ResourceManager的服務初始化程式碼:
this.amRmTokenSecretManager = createAMRMTokenSecretManager(conf);
我們看到內部有個這個成員變數:
/**
* AMRM-tokens are per ApplicationAttempt. If users redistribute their
* tokens, it is their headache, god save them. I mean you are not supposed to
* distribute keys to your vault, right? Anyways, ResourceManager saves each
* token locally in memory till application finishes and to a store for restart,
* so no need to remember master-keys even after rolling them.
*/
public class AMRMTokenSecretManager extends
SecretManager<AMRMTokenIdentifier>
看註釋,清晰明瞭,每次提交一個ApplicationAttempt時候,都不用再遞交自己的token了,實際上還是一個身份驗證工具(自己的理解)。
this.containerAllocationExpirer = new ContainerAllocationExpirer(this.rmDispatcher);
addService(this.containerAllocationExpirer);
看下這兩句話ContainerAllocationExpirer,並且加到了serviceList中,用於最後的初始化,我們看看這個是什麼作用,註釋非常簡單,還是留在動態提交ApplicationMaster的時候分析吧,其實主要是用來判斷分配的container是否在規定時間內得到啟動的:
AMLivelinessMonitor amLivelinessMonitor = createAMLivelinessMonitor();
addService(amLivelinessMonitor);
看這個AMLiveLinessMonitor,顧名思義,是用來檢查ApplicationLivenessMonitor是否存活的,其繼承了這個類:
/**
* A simple liveliness monitor with which clients can register, trust the
* component to monitor liveliness, get a call-back on expiry and then finally
* unregister.
*/
@Public
@Evolving
public abstract class AbstractLivelinessMonitor<O> extends AbstractService
同時,ContainerAllocationMonitor也繼承了這個類,就是用於讓客戶端監控的:
AMLivelinessMonitor amFinishingMonitor = createAMLivelinessMonitor();
addService(amFinishingMonitor);
下面初始化了一個一樣的AMLiveLinessMonitor,但是變數名不同,同樣是起監控作用的:
接下來看RMStateStore的初始化:
boolean isRecoveryEnabled = conf.getBoolean(YarnConfiguration.RECOVERY_ENABLED,
YarnConfiguration.DEFAULT_RM_RECOVERY_ENABLED);
RMStateStore rmStore = null;
if (isRecoveryEnabled) {
recoveryEnabled = true;
rmStore = RMStateStoreFactory.getStore(conf);
} else {
recoveryEnabled = false;
rmStore = new NullRMStateStore();
}
對於大部分成員變數的初始化不予多說,先看下RMStateStore的初始化,在我們預設配置下:isRecoveryEnabled為false,所以建立了一個空的RMStateStore,即NullRMStateStore,對於ResourceManager的狀態進行儲存:
rmStore.init(conf);
rmStore.setRMDispatcher(rmDispatcher);
這裡面注意下,rmStore內部的dispatcher與RM的dispatcher不是同一個,程式碼如下:
private Dispatcher rmDispatcher;
AsyncDispatcher dispatcher;
RMStateStore內部有兩個排程器,rmDispatcher是RM的排程器,而dispatcher則是其內部用來排程事件的排程器,對於RMStateStore的init程式碼有些繞,仔細看下:
@Override
public void init(Configuration conf) {
if (conf == null) {
throw new ServiceStateException("Cannot initialize service " + getName() + ": null configuration");
}
if (isInState(STATE.INITED)) {
return;
}
synchronized (stateChangeLock) {
if (enterState(STATE.INITED) != STATE.INITED) {
setConfig(conf);
try {
serviceInit(config);
if (isInState(STATE.INITED)) {
// if the service ended up here during init,
// notify the listeners
notifyListeners();
}
} catch (Exception e) {
noteFailure(e);
ServiceOperations.stopQuietly(LOG, this);
throw ServiceStateException.convert(e);
}
}
}
}
其實際呼叫的是AbstractService的init方法,其中呼叫到了serviceInit方法,而這個方法,則是RMStateStore的方法:
public synchronized void serviceInit(Configuration conf) throws Exception {
// create async handler
dispatcher = new AsyncDispatcher();
dispatcher.init(conf);
dispatcher.register(RMStateStoreEventType.class, new ForwardingEventHandler());
initInternal(conf);
}
很清楚看到了內部封裝了一個自己的dispatcher,用於排程RMStateStoreEventType型別的事件:
下面接著看:
this.rmContext = new RMContextImpl(this.rmDispatcher, rmStore, this.containerAllocationExpirer,
amLivelinessMonitor, amFinishingMonitor, delegationTokenRenewer, this.amRmTokenSecretManager,
this.containerTokenSecretManager, this.nmTokenSecretManager, this.clientToAMSecretManager);
這是重頭戲,我們必須看看這個擁有如此多成員變數的RMContextImpl到底是什麼:
/**
* Context of the ResourceManager.
*/
public interface RMContext {
這是RMContextImpl父類的註釋,是ResourceManager的上下文,就相當於管家了,基本大權在握,是ResourceManager的心腹。
接下來是這兒:
this.nodesListManager = new NodesListManager(this.rmContext);
其實就相當於告訴了RM,這裡到底有多少個子節點可供使用,而且給新建的NodesListManager內部也安插了RM的心腹,即RMContextImpl。
this.rmDispatcher.register(NodesListManagerEventType.class, this.nodesListManager);
看到這兒,我們又得回去看AsyncDispatcher中的一個register方法:
@SuppressWarnings("unchecked")
@Override
public void register(Class<? extends Enum> eventType, EventHandler handler) {
/* check to see if we have a listener registered */
EventHandler<Event> registeredHandler = (EventHandler<Event>) eventDispatchers.get(eventType);
LOG.info("Registering " + eventType + " for " + handler.getClass());
if (registeredHandler == null) {
eventDispatchers.put(eventType, handler);
} else if (!(registeredHandler instanceof MultiListenerHandler)) {
/* for multiple listeners of an event add the multiple listener handler */
MultiListenerHandler multiHandler = new MultiListenerHandler();
multiHandler.addHandler(registeredHandler);
multiHandler.addHandler(handler);
eventDispatchers.put(eventType, multiHandler);
} else {
/* already a multilistener, just add to it */
MultiListenerHandler multiHandler = (MultiListenerHandler) registeredHandler;
multiHandler.addHandler(handler);
}
}
仔細看來,其實就相當於eventDispatcher的充實,把各類事件即相應的處理,都送給eventdispatcher,方便後續出現類似事件,eventdispatcher能夠迅速找到對應的物件來進行處理。
這裡,就相當於把對應於NodeManager出現的事情,都交給了NodeListManager,這就是你的工作了。
public enum NodesListManagerEventType {
NODE_USABLE, NODE_UNUSABLE
}
如果出現了節點可用和不可用的事情,你就得迅速予以處理了。
// Initialize the scheduler
this.scheduler = createScheduler();
this.schedulerDispatcher = createSchedulerEventDispatcher();
addIfService(this.schedulerDispatcher);
this.rmDispatcher.register(SchedulerEventType.class, this.schedulerDispatcher);
接下來,建立了一個排程器,這一段得仔細看看了,因為yarn中的排程器非常重要,我們作業的初始化,都離不開它:
protected ResourceScheduler createScheduler() {
String schedulerClassName = conf.get(YarnConfiguration.RM_SCHEDULER, YarnConfiguration.DEFAULT_RM_SCHEDULER);
LOG.info("Using Scheduler: " + schedulerClassName);
try {
Class<?> schedulerClazz = Class.forName(schedulerClassName);
if (ResourceScheduler.class.isAssignableFrom(schedulerClazz)) {
return (ResourceScheduler) ReflectionUtils.newInstance(schedulerClazz, this.conf);
} else {
throw new YarnRuntimeException("Class: " + schedulerClassName + " not instance of "
+ ResourceScheduler.class.getCanonicalName());
}
} catch (ClassNotFoundException e) {
throw new YarnRuntimeException("Could not instantiate Scheduler: " + schedulerClassName, e);
}
}
我這裡的程式碼是hadoop 2.2.0,在預設配置下,配置的排程器是:
org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler
而接著,我們給排程器自己定義了一個排程器,換句話說,對於排程器接受的事件型別,其會繼續排程給其他的handler去處理
最後,把這個排程器也註冊給了RM內部的排程器:
protected EventHandler<SchedulerEvent> createSchedulerEventDispatcher() {
return new SchedulerEventDispatcher(this.scheduler);
}
this.rmDispatcher.register(SchedulerEventType.class, this.schedulerDispatcher);
接著,我們的非同步排程器又加入了三個成分,負責對Application相關事件,ApplicationAttempt事件,RMNode事件進行排程:
// Register event handler for RmAppEvents
this.rmDispatcher.register(RMAppEventType.class, new ApplicationEventDispatcher(this.rmContext));
// Register event handler for RmAppAttemptEvents
this.rmDispatcher.register(RMAppAttemptEventType.class, new ApplicationAttemptEventDispatcher(this.rmContext));
// Register event handler for RmNodes
this.rmDispatcher.register(RMNodeEventType.class, new NodeEventDispatcher(this.rmContext));
接著,我們看下這個:
this.resourceTracker = createResourceTrackerService();
addService(resourceTracker);
從官方文件中,我們知道RM實現了對於系統全部資源的管控,而這個管控是通過RPC來實現的,NodeManager呼叫ResourceTracker內的方法來提交自己的資源,而RM端有相應的處理,返回命令,讓NM予以執行,而ResourceTrackerSerive就是在此處初始化的:
@Override
protected void serviceInit(Configuration conf) throws Exception {
resourceTrackerAddress = conf.getSocketAddr(YarnConfiguration.RM_RESOURCE_TRACKER_ADDRESS,
YarnConfiguration.DEFAULT_RM_RESOURCE_TRACKER_ADDRESS,
YarnConfiguration.DEFAULT_RM_RESOURCE_TRACKER_PORT);
RackResolver.init(conf);
nextHeartBeatInterval = conf.getLong(YarnConfiguration.RM_NM_HEARTBEAT_INTERVAL_MS,
YarnConfiguration.DEFAULT_RM_NM_HEARTBEAT_INTERVAL_MS);
if (nextHeartBeatInterval <= 0) {
throw new YarnRuntimeException("Invalid Configuration. " + YarnConfiguration.RM_NM_HEARTBEAT_INTERVAL_MS
+ " should be larger than 0.");
}
minAllocMb = conf.getInt(YarnConfiguration.RM_SCHEDULER_MINIMUM_ALLOCATION_MB,
YarnConfiguration.DEFAULT_RM_SCHEDULER_MINIMUM_ALLOCATION_MB);
minAllocVcores = conf.getInt(YarnConfiguration.RM_SCHEDULER_MINIMUM_ALLOCATION_VCORES,
YarnConfiguration.DEFAULT_RM_SCHEDULER_MINIMUM_ALLOCATION_VCORES);
super.serviceInit(conf);
}
可以看到,這裡牽涉到了很多的預設配置的地址,所以,看程式碼是必要的,我們想要把配置檔案全部搞通,其實認真搞通程式碼,就輕而易舉了。
masterService = createApplicationMasterService();
addService(masterService);
看看這一段,新建了一個ApplicationMasterService,用於對所有提交的ApplicationMaster進行管理:其中的serviceInit方法不復雜,重點在其serviceStart方法中:
下面,我們注意看下這段程式碼:
this.rmAppManager = createRMAppManager();
// Register event handler for RMAppManagerEvents
this.rmDispatcher.register(RMAppManagerEventType.class, this.rmAppManager);
this.rmDTSecretManager = createRMDelegationTokenSecretManager(this.rmContext);
rmContext.setRMDelegationTokenSecretManager(this.rmDTSecretManager);
clientRM = createClientRMService();
rmContext.setClientRMService(clientRM);
addService(clientRM);
adminService = createAdminService(clientRM, masterService, resourceTracker);
addService(adminService);
this.applicationMasterLauncher = createAMLauncher();
this.rmDispatcher.register(AMLauncherEventType.class, this.applicationMasterLauncher);
這裡有些東西需要注意,新建了一個ClientRMService,負責客戶端與RM的所有互動,對於客戶端的每個請求,我們都可以在ClientRMService下面找到相應的程式碼。
下面接著看,AMLauncher,這個很重要,負責ApplicationMaster的啟動,必須重點分析下。
this.applicationMasterLauncher = createAMLauncher();
this.rmDispatcher.register(AMLauncherEventType.class, this.applicationMasterLauncher);
基於RM的管家,建立了一個AMLauncher:
protected ApplicationMasterLauncher createAMLauncher() {
return new ApplicationMasterLauncher(this.rmContext);
}
分析下其中的serviceInit方法:
@Override
protected void serviceStart() throws Exception {
launcherHandlingThread.start();
super.serviceStart();
}
看看其中的launchHandlingThread:
private class LauncherThread extends Thread {
public LauncherThread() {
super("ApplicationMaster Launcher");
}
@Override
public void run() {
while (!this.isInterrupted()) {
Runnable toLaunch;
try {
toLaunch = masterEvents.take();
launcherPool.execute(toLaunch);
} catch (InterruptedException e) {
LOG.warn(this.getClass().getName() + " interrupted. Returning.");
return;
}
}
}
}
其內部封裝了一個執行緒池,對於排程給自身的事件不斷進行處理:
在serviceInit方法的最後,呼叫了父類的serviceInit方法,我們看下其父類CompositeService的serviceInit方法:
protected void serviceInit(Configuration conf) throws Exception {
List<Service> services = getServices();
if (LOG.isDebugEnabled()) {
LOG.debug(getName() + ": initing services, size=" + services.size());
}
for (Service service : services) {
service.init(conf);
}
super.serviceInit(conf);
}
public List<Service> getServices() {
synchronized (serviceList) {
return Collections.unmodifiableList(serviceList);
}
}
在看原始碼的時候,發現RM的serviceInit方法中,所有服務都有一個操作:
protected void addService(Service service) {
if (LOG.isDebugEnabled()) {
LOG.debug("Adding service " + service.getName());
}
synchronized (serviceList) {
serviceList.add(service);
}
}
呼叫了父類中的addService方法,把所有服務新增到了serviceList中,在這裡統一予以初始化,不得不說設計很精妙,在我們分析原始碼的時候,必須注意看下service相關的類;最頂層的父類是Serivce類,這是個介面,定義了服務的基本操作和生命週期,其唯一的實現類,是個抽象類,為AbstractService,一般來說,並不複雜的服務繼承並實現AbstractService即可,複雜的服務如RM就會繼承Compositeservice(AbstractService的子類)。
在RM服務初始化完畢之後,我們接著看服務的啟動部分。
相關推薦
關於Yarn原始碼那些事-前傳之ResourceManager篇(一)初始化
在關於Yarn那些事的部落格裡,介紹的主要是針對任務提交的一個動態流程說明,而其中牽涉到的一些細節問題,必須通過Resourcemanager的啟動和NodeManager的啟動,來更好的說明。而本系列,就詳細說說ResourceManager啟動過程中,都發生了什麼。我們都
SpringBoot原始碼之事物篇(一)事物是在哪裡開啟的呢?
最近正在學習使用JPA,JPA是預設開啟事物管理的,在哪裡開啟的呢???突然覺得對spring處理事物的原理一竊不通,才有了這次的原始碼閱讀之行,有不正確的地方歡迎大家指正。 先簡略過一下springboot的執行原理 public void pr
Zabbix3.4之安裝篇(一)
entos agent roo zabb com serve arc centos 7 mysql- 實驗環境:Centos 71.下載zabbix yum 文件 [root@xiaopeng /]# cd /etc/yum.repos.d/ [root@xiaop
Zabbix 3.4之 安裝篇(一)
systemctl 防火墻 sql數據庫 x86_64 mage system 啟動 輸入密碼 shang 實驗環境:Centos 7```1.下載zabbix yum 文件[root@xiaopeng /]# cd /etc/yum.repos.d/[root@x
淺析 rand7生成rand10 方法 之 思想篇(一)
同余 one a + b 個數 height 意義 UC ogr edi 【問題描寫敘述】 rand7是一個能生成1-7的隨機數。要求利用rand7生成1-10的隨
構建NetCore應用框架之實戰篇(一):什麽是框架,如何設計一個框架
net 希望 dmi 清晰 構建 組織 評估 系統開發 概念 一、系列簡述 本篇起,將通過一系列文章,去描述如何構建一個應用開發框架,並以作者開發的框架為例,逐個點展開分析,如何從零開始,構建自己的開發框架。 本系列文章的目的,是帶領有一編程經驗的人,通過動手,初步完成
scala隨筆之入門篇(一)
scala 簡介 Java 方言之一,java的方言除了scala還有kotlin、groovy、clojure等。 執行在JVM之上 scala和kotlin、groovy、clojure一樣是多正規化程式語言,支援函數語言程式設計 scala常用領域是併發程
Elam的caffe筆記之配置篇(一):CentOS6.5編譯安裝gcc4.8.2
Elam的caffe筆記之配置篇(一):CentOS6.5編譯安裝gcc4.8.2 配置要求: 系統:centos6.5 目標:基於CUDA8.0+Opencv3.1+Cudnnv5.1+python3.6介面的caffe框架 任何對linux處於入門級別的小白都應
運維經典面試題之網路篇(一)
1、寫出12.23.34.0/29的掩碼 11111111.11111111.11111111.11111000 255.255.255.248 2、簡述衝突域與廣播域的區別 衝突域:基於osi的第一層物理層。 一個站點向另一個站
Java多執行緒之基礎篇(一)
一、併發和並行 1.1 概念 1.2 比較 1.3 程序和執行緒 二、基礎概念 2.1 執
Machine Learning之Python篇(一)
Machine Learning之Python篇 概述 教程 《Python機器學習》中文版 東南大學某研究生的github,包含大量ML演算法示例。 上個哥們的DL示例 Python資料分析之武林祕籍。這裡包括了大量ML或DL的python工具包。
CSS3筆記之定位篇(一)relative
知識點1:relative和absolute relative: 相對自身,並會限制內部absolute元素層疊 absolute: 相對容器,並受到父類容器relative的影響,比如:o
部署Django部落格全記錄之Nginx篇(一)
Nginx的優點 注:具體原理沒搞清,先記著 Nginx更安全;Nginx能更好地處理靜態資源(通過一些http request header)。 Nginx也可以快取一些動態內容;Nginx可以更好地配合CDN。 Nginx可以進行多臺機器的負載均
Spring Boot 入門之基礎篇(一)
一、前言 Spring Boot 是由 Pivotal 團隊提供的全新框架,其設計目的是用來簡化新 Spring 應用的初始搭建以及開發過程。該框架使用了特定的方式來進行配置,從而使開發人員不再需要定義樣板化的配置。 本系列以快速入門為主,可當作工具小手冊閱讀
Windows驅動開發之入門篇(一)
本文主要介紹“如何入門Windows驅動開發”和“新手需要掌握哪些知識和動手技能”,大部分是本人探索Windows驅動開發近一月時間的經驗之談。大致包括如下幾個方面: 1,開發工具、除錯工具和除錯手段; 2,Windows驅動開發框架; 3,驅動程式基本知識
FPGA設計之硬體篇(一)
完成整個硬體電路板的設計後,接下來就是程式的設計了,這裡面靈活性很大,在設計中一定要注意語法的嚴謹性,一個小小的“<="都會帶來無盡的麻煩,在完成程式設計後,一定要進行時序模擬,我一般是先把模組分塊利用工具內部波形模擬來檢視時序,接著採用SIGNAL TAP II進行內部邏輯分析,這個花了太多
小程式之入門篇(一)
大家好,我是小編,這幾天自己學習了小程式,發現真的太爽了,以下是我寫下的筆記,希望給入門的盆友提供一些幫助吧。 app.js 用於監聽並處理小程式的生命週期函式,宣告全域性變數,呼叫框架,提供豐富的API 同步儲存:wx.setStorageSync()
Linux下安裝Lnmp環境之準備篇(一)
1、準備 1.1、安裝lrzsz yum install lrzsz -y # rz 上傳 sz PATH 1.2、配置防火牆 vi /etc/sysconfig/iptables #編輯防火牆配置檔案 把以下內容貼上進去 #
Spring Boot 學習之基礎篇(一)
該系列並非完全原創,官方文件、作者Spring Boot 是由 Pivotal 團隊提供的全新框架,其設計目的是用來簡化新 Spring 應用的初始搭建以及開發過程。該框架使用了特定的方式來進行配置,從而使開發人員不再需要定義樣板化的配置。一、環境搭建建立一個Maven專案,
Revit二次開發之技能篇(一)———軸網自動對齊
在做專案的過程中,軸網都是進行翻模或者拾取,而翻模出來的軸網往往都是不堪入目的,為了滿足專案需要,做了軸網對齊的功能,廢話不多說,直接上乾貨。 首先做一些準備工作,先建立一個軸網過濾類,接下來會用到,程式碼如下: class GridSelectionFilter : ISelection