Qualcomm平臺qcril初始化及訊息處理流程(原)
本節主要來介紹Qcril的初始化流程以及訊息在Qcril中如何傳遞。
Android平臺不同廠商的AP側可以相同,但是Modem側肯定會有很大的差異,RIL層要解決一個問題就是適配不同廠商的Modem,為了達到相容性要求,Android在AP與Modem之間搭建了RILC的框架,由不同的Modem廠商將自己的協議連線到AP側。對於高通平臺來說,他的RILC就是QCRIL。
在Qcril中儲存一個靜態表單,裡面儲存了所有RILC中下發請求的ID以及相應的處理函式,表單內容簡要如下:裡面每一項都包含兩個元素:事件ID和處理函式,在處理這些訊息時將會根據事件的ID查詢並執行相應的處理函式。static qcril_dispatch_table_entry_type qcril_event_table[] = { /* QCRIL_EVT_UIM_QMI_COMMAND_CALLBACK */ { QCRIL_REG_ALL_STATES( QCRIL_EVT_UIM_QMI_COMMAND_CALLBACK, qcril_uim_process_qmi_callback ) }, /* QCRIL_EVT_UIM_QMI_INDICATION */ { QCRIL_REG_ALL_STATES( QCRIL_EVT_UIM_QMI_INDICATION, qcril_uim_process_qmi_indication ) }, /* QCRIL_EVT_INTERNAL_UIM_VERIFY_PIN_COMMAND_CALLBACK */ { QCRIL_REG_ALL_STATES( QCRIL_EVT_INTERNAL_UIM_VERIFY_PIN_COMMAND_CALLBACK, qcril_uim_process_internal_command ) }, /* QCRIL_EVT_INTERNAL_MMGSDI_CARD_POWER_UP */ { QCRIL_REG_ALL_STATES( QCRIL_EVT_INTERNAL_MMGSDI_CARD_POWER_UP, qcril_uim_process_internal_command ) }, /* 0x90007 - QCRIL_EVT_HOOK_OEM_ENG_MODE */ { QCRIL_REG_ALL_ACTIVE_STATES( QCRIL_EVT_HOOK_OEM_ENG_MODE, qcril_qmi_nas_request_eng_mode_info ) }, /* 1 - RIL_REQUEST_GET_SIM_STATUS */ { QCRIL_REG_ALL_ACTIVE_STATES( RIL_REQUEST_GET_SIM_STATUS, qcril_uim_request_get_sim_status ) }, /* 2 - RIL_REQUEST_ENTER_SIM_PIN */ { QCRIL_REG_ALL_ACTIVE_STATES( RIL_REQUEST_ENTER_SIM_PIN, qcril_uim_request_enter_pin ) }, /* 105 - RIL_REQUEST_ISIM_AUTHENTICATION */ { QCRIL_REG_ALL_ACTIVE_STATES( RIL_REQUEST_ISIM_AUTHENTICATION, qcril_uim_request_isim_authenticate ) }, }
比如,對於得到當前SIM卡狀態這個請求,對應的ID為RIL_REQUEST_GET_SIM_STATUS,而其處理函式為:qcril_uim_request_get_sim_status()。
一、Qcril初始化流程
初始化流程需要完成EventLoop訊息迴圈的建立、各個模組的初始化等工作。先看RILD部分。在RILD中會通過dlsym查詢ril庫中的RIL_Init函式地址,然後通過rilInit呼叫,對高通來說,該函式在qcril.c中:@rild.c int main(int argc, char **argv) { //動態載入reference-ril.so或者qcril.so dlHandle = dlopen(rilLibPath, RTLD_NOW); //建立Loop監聽Socket事件 RIL_startEventLoop(); rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init"); funcs = rilInit(&s_rilEnv, argc, rilArgv); RIL_register(funcs); }
@qcril.c const RIL_RadioFunctions *RIL_Init ( const struct RIL_Env *env, int argc, char **argv) { //設定執行緒名字為rild qmi_ril_set_thread_name( pthread_self() , QMI_RIL_QMI_RILD_THREAD_NAME); qmi_ril_fw_android_request_flow_control_init(); //初始化unsol的eventlist pending_unsol_resp_list,為其分配記憶體 qmi_ril_init_android_unsol_resp_pending_list(); //初始化接收Modem訊息的EventLoop qcril_event_init(); //初始化qcril中的各個模組 qcril_init(); //開啟EventLoop qcril_event_start(); //其他初始化 qmi_ril_initiate_bootup(); //返回RILD對RILC的介面函式 return &qcril_request_api[ QCRIL_DEFAULT_INSTANCE_ID ]; }
下面我們分別來分析上面的過程。
1.1、初始化EventLoop過程
在Qcril中搭建了EventLoop迴圈用於檢測Modem上報的訊息,而EventLoop機制的初始化工作是在qcril_event_init()中完成的。 @qcril_event.c
void qcril_event_init( void ) {
pthread_attr_t attr;
int ret;
qcril_event.started = 0;
#ifdef QMI_RIL_UTF
pthread_attr_init (&attr);
ret = utf_pthread_create_handler(&qcril_event.tid, &attr, qcril_event_main, NULL);
pthread_attr_destroy( &attr );
#else
pthread_attr_init (&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
//建立EventLoop執行緒,執行緒入口是qcril_event_main
ret = pthread_create(&qcril_event.tid, &attr, qcril_event_main, NULL);
pthread_attr_destroy( &attr );
#endif
//設定執行緒名字為"event"
qmi_ril_set_thread_name(qcril_event.tid, QMI_RIL_EVENT_THREAD_NAME);
pthread_mutexattr_init( &qcril_event.activity_lock_mutex_atr );
pthread_mutex_init( &qcril_event.activity_lock_mutex, &qcril_event.activity_lock_mutex_atr );
while (qcril_event.started == 0)
{
pthread_cond_wait(&qcril_event_startupCond, &qcril_event.startup_mutex);
}
}
在初始化過程中,通過pthread_create()函式建立了EventLoop執行緒,並且指出該執行緒的入口函式為qcril_event_main(),我們從執行緒的入口開始分析: static void *qcril_event_main ( void *param) {
int ret;
int filedes[2];
int n;
fd_set rfds;
qcril_event_type *ev;
char buff[16];
IxErrnoType err_no;
int go_on;
param = param;
pthread_mutex_init(&qcril_event.list_mutex, NULL);
//初始化qcril_event.list連結串列
qcril_event_init_list(&qcril_event.list);
FD_ZERO(&qcril_event.readFds); /* Needed to use select() system call */
QCRIL_MUTEX_LOCK( &qcril_event.startup_mutex, "[Event Thread] qcril_event.startup_mutex" );
qcril_event.started = 1;
//建立管道
ret = pipe(filedes);
qcril_event.fdWakeupRead = filedes[0];
qcril_event.fdWakeupWrite = filedes[1];
fcntl(qcril_event.fdWakeupRead, F_SETFL, O_NONBLOCK);
FD_SET(qcril_event.fdWakeupRead, &qcril_event.readFds);
pthread_cond_broadcast(&qcril_event_startupCond);
while (qcril_event.started < 2)
{
//阻塞等待qcril初始化
pthread_cond_wait(&qcril_event_startupCond, &qcril_event.startup_mutex);
}
for (;;)
{
/* Make a local copy of read fd_set; Don't ask why. */
memcpy(&rfds, &qcril_event.readFds, sizeof(fd_set));
//阻塞等待接收內容
n = select(qcril_event.fdWakeupRead + 1, &rfds, NULL, NULL, NULL);
if (n < 0)
{
if (errno == EINTR) continue;
QCRIL_LOG_ERROR("QCRIL event select error (%d)", errno);
qmi_ril_clear_thread_name(pthread_self());
return NULL;
}
/* Empty the socket */
do
{
//讀取內容
ret = read(qcril_event.fdWakeupRead, &buff, sizeof(buff));
} while (ret > 0 || (ret < 0 && errno == EINTR));
do
{
if ( ( NULL != ( ev = qcril_event.list.next ) && ( ev != &qcril_event.list ) ) )
{
qcril_event_remove_from_list( ev );
QCRIL_MUTEX_UNLOCK( &qcril_event.list_mutex, "[Event Thread] qcril_event.list_mutex" );
//處理Modem傳送的請求
err_no = qcril_process_event( ev->instance_id, ev->modem_id, ev->event_id, ev->data, ev->datalen, ev->t );
QCRIL_MUTEX_LOCK( &qcril_event.list_mutex, "[Event Thread] qcril_event.list_mutex" );
if ( ev->data_must_be_freed && ev->data )
{
qcril_free( ev->data );
}
qcril_free( ev );
}
go_on = ( ( NULL != ( ev = qcril_event.list.next ) && ( ev != &qcril_event.list ) ) );
} while ( go_on );
}
qmi_ril_clear_thread_name(pthread_self());
return NULL;
}
在以上過程中,完成qcril_event.list連結串列的初始化,然後通過pthread_cond_wait進入阻塞狀態,當被解鎖後以及進入EventLoop迴圈,檢測到事件後,通過qcril_process_event處理。1.2、初始化qcril各個模組
Qcril在接到RILC的請求後,需要根據請求的型別將訊息派發給不同的負責模組,而qcril_init()就是完成各個模組的初始化工作。 void qcril_init ( void) {
qcril_arb_init();
qcril_init_state();
qmi_ril_oem_hook_init();
qcril_db_init();
//初始化Event table
qcril_init_hash_table();
qcril_reqlist_init();
#ifdef FEATURE_QCRIL_PLMN_LIST
qcril_qmi_nas2_init();
#endif
qcril_request_suppress_list_init();
qmi_ril_qmi_client_pre_initialization_init();
qmi_ril_qmi_client_pre_initialization_acquire();
qcril_qmi_nas_dms_commmon_pre_init();
qcril_qmi_voice_pre_init();
#ifndef QMI_RIL_UTF
qcril_am_pre_init();
#else
qmi_ril_rat_enable_option = QMI_RIL_FTR_RAT_UNKNOWN;
qmi_ril_baseband_ftr_info = QMI_RIL_FTR_BASEBAND_UNKNOWN;
#endif
qcril_qmi_imsa_pre_init();
qcril_qmi_sms_pre_init();
QCRIL_LOG_FUNC_RETURN();
}
在這裡對qcril的各個模組進行初始化。其中完成了很重要的一步就是將qcril_event_table表拷貝給qcril_hash_table,用於onRequest時對各種請求進行處理,我們來看具體操作: static void qcril_init_hash_table( void ) {
uint32 reg_index, hash_index; /*!< index into hash table */
qcril_dispatch_table_entry_type *temp_entry_ptr;
for (reg_index = 0; reg_index < QCRIL_ARR_SIZE( qcril_event_table ); reg_index++)
{
hash_index = qcril_hash( qcril_event_table[reg_index].event_id, QCRIL_HT_ENTRIES_MAX, 0 );
if(hash_index < QCRIL_HT_ENTRIES_MAX)
{
if (qcril_hash_table[hash_index] == NULL)
{
//將qcril_event_table拷貝給qcril_hash_table
qcril_hash_table[hash_index] = &qcril_event_table[reg_index];
}
else
{
temp_entry_ptr = qcril_hash_table[hash_index];
while (temp_entry_ptr->next_ptr != NULL)
{
temp_entry_ptr = temp_entry_ptr->next_ptr;
}
temp_entry_ptr->next_ptr = &qcril_event_table[reg_index];
}
}
}
}
經過上面的拷貝,qcril_event_table中就儲存了所有Request的id和處理方法。
1.3、開啟EventLoop
在1.1中介紹過,初始化EventLoop時,在完成其連結串列的初始化過程後,通過pthread_cond_wait()將其阻塞,而現在要做的就是取消其阻塞狀態,使其進入訊息檢測迴圈。這是在qcril_event_start()中完成的:
void qcril_event_start( void )
{
QCRIL_MUTEX_LOCK( &qcril_event.startup_mutex, "[Main Thread] qcril_event.startup_mutex" );
//更新狀態
qcril_event.started = 2;
//釋放EventLoop鎖
pthread_cond_broadcast(&qcril_event_startupCond);
QCRIL_MUTEX_UNLOCK( &qcril_event.startup_mutex, "[Main Thread] qcril_event.startup_mutex" );
}
由於EventLoop被初始化後一直處於阻塞狀態,所以在這裡將started狀態置為2後,對qcril_event_startupCond進行解鎖,從而使EventLoop進入迴圈。
1.4、其他初始化過程
在qmi_ril_initiate_bootup()中完成了一些其他的初始化流程。
void qmi_ril_initiate_bootup(void)
{
qcril_setup_timed_callback( QCRIL_DEFAULT_INSTANCE_ID, QCRIL_DEFAULT_MODEM_ID, qmi_ril_bootup_perform_core_or_start_polling, NULL, NULL );
}
繼續看qmi_ril_bootup_perform_core_or_start_polling()過程: void qmi_ril_bootup_perform_core_or_start_polling(void * params)
{
RIL_Errno init_res;
int ril_version;
qcril_unsol_resp_params_type unsol_resp;
qmi_ril_main_thread_id = pthread_self();
qmi_ril_set_thread_name( qmi_ril_fw_get_main_thread_id(), QMI_RIL_QMI_MAIN_THREAD_NAME);
qmi_ril_wave_modem_status(); // this should result in "modem unavailble" report
qmi_ril_set_operational_status( QMI_RIL_GEN_OPERATIONAL_STATUS_INIT_PENDING ); // for consistency
qmi_ril_set_operational_status( QMI_RIL_GEN_OPERATIONAL_STATUS_INIT_ONGOING );
//qmi初始化
init_res = qmi_ril_core_init();
}
上面通過qmi_ril_core_init()完成了qmi的初始化: RIL_Errno qmi_ril_core_init(void)
{
RIL_Errno res = RIL_E_GENERIC_FAILURE;
QCRIL_LOG_FUNC_ENTRY();
qcril_event_suspend(); // to ensure atomic init flow cross sub domains
do
{
//qcril client的初始化
res = qcril_qmi_client_init();
if ( RIL_E_SUCCESS != res )
break;
qcril_other_init();
qcril_uim_init();
qcril_gstk_qmi_init();
#ifndef QMI_RIL_UTF
qcril_data_init();
#endif
qcril_qmi_nas_dms_commmon_post_init();
if (qmi_ril_is_feature_supported(QMI_RIL_FEATURE_OEM_SOCKET))
{
QCRIL_LOG_INFO( "%s Init OEM socket thread", __FUNCTION__ );
qcril_qmi_oem_socket_init();
}
} while (FALSE);
qcril_event_resume();
QCRIL_LOG_FUNC_RETURN_WITH_RET(res);
return res;
}
在上面完成了qcril客戶端的初始化過程: RIL_Errno qcril_qmi_client_init( void )
{
qmi_client_error_type client_err = 0;
RIL_Errno res = RIL_E_GENERIC_FAILURE;
QCRIL_LOG_FUNC_ENTRY();
/* Start modem or vote for start modem */
qcril_qmi_modem_power_process_bootup();
memset(&client_info, 0, sizeof(client_info));
do
{
// QMI VOICE command callback
client_info.client_cbs[QCRIL_QMI_CLIENT_VOICE] = qcril_qmi_voice_command_cb;
// Get IDL service objects
client_info.service_objects[QCRIL_QMI_CLIENT_VOICE] = voice_get_service_object_v02();
client_info.service_objects[QCRIL_QMI_CLIENT_NAS] = nas_get_service_object_v01();
client_info.service_objects[QCRIL_QMI_CLIENT_WMS] = wms_get_service_object_v01();
client_info.service_objects[QCRIL_QMI_CLIENT_WDS] = wds_get_service_object_v01();
client_info.service_objects[QCRIL_QMI_CLIENT_DMS] = dms_get_service_object_v01();
/*client_info.service_objects[QCRIL_QMI_CLIENT_UIM] = uim_get_service_object_v01();*/
client_info.service_objects[QCRIL_QMI_CLIENT_PBM] = pbm_get_service_object_v01();
client_info.service_objects[QCRIL_QMI_CLIENT_RF_SAR] = sar_get_service_object_v01();
client_info.service_objects[QCRIL_QMI_CLIENT_IMS_VT] = ims_qmi_get_service_object_v01();
client_info.service_objects[QCRIL_QMI_CLIENT_IMS_PRESENCE] = imsp_get_service_object_v01();
client_info.service_objects[QCRIL_QMI_CLIENT_IMSA] = imsa_get_service_object_v01();
client_info.service_objects[QCRIL_QMI_CLIENT_RFPE] = rfrpe_get_service_object_v01();
client_info.service_objects[QCRIL_QMI_CLIENT_IMS_SETTING] = imss_get_service_object_v01();
if ( qmi_ril_get_process_instance_id() == QCRIL_DEFAULT_INSTANCE_ID )
{
client_info.service_objects[QCRIL_QMI_CLIENT_PDC] = pdc_get_service_object_v01();
}
pthread_mutexattr_init(&client_info.cache_lock_mtx_atr);
pthread_mutex_init(&client_info.cache_lock_mutex, &client_info.cache_lock_mtx_atr);
res = qcril_qmi_init_core_client_handles();
if (RIL_E_SUCCESS != res)
break;
} while (FALSE);
return res;
}
1.5、將回調函式註冊給RILC
在Qcril的初始化完畢後,將自己的函式列表返回給RilC,也就是qcril_request_api: static const RIL_RadioFunctions qcril_request_api[] = {
{ RIL_VERSION, onRequest_rid, currentState_rid, onSupports_rid, onCancel_rid, getVersion_rid }
};
這樣的話,在RIL中呼叫的介面就會進入該函式列表中進行處理。以上就是qcril的初始化流程。
二、QCRIL對請求的處理過程
當ril有請求過來時,就會呼叫ril庫的onRequest()方法,此時就會根據當前Qcril註冊的函式列表進入到qcril_request_api的onRequest_rid()函式中: @qcril.c
static void onRequest_rid ( int request, void *data, size_t datalen, RIL_Token t)
{
onRequest( qmi_ril_process_instance_id, request, data, datalen, t );
}
然後進入onRequest()中繼續處理: static void onRequest ( qcril_instance_id_e_type instance_id, int request, void *data, size_t datalen, RIL_Token t) {
udit_result = qmi_ril_fw_android_request_render_execution( param.t,
param.event_id,
param.data,
param.datalen,
param.instance_id,
&log_dispatch_dedicated_thrd );
}
繼續: RIL_Errno qmi_ril_fw_android_request_render_execution( RIL_Token token, int android_request_id, void * android_request_data, int android_request_data_len, qcril_instance_id_e_type instance_id, int * is_dedicated_thread ) {
do
{
entry_ptr = NULL;
//從hash表中查詢當前的Event
if ( qcril_hash_table_lookup( (uint32) param.event_id, &entry_ptr ) != E_SUCCESS || NULL == entry_ptr )
{
audit_result = RIL_E_REQUEST_NOT_SUPPORTED;
break;
}
if ( dedicated_thrd_req_lookup_val == param.event_id )
{ // deferred thread exec
}
else
{
//派發該Event
if ( qcril_dispatch_event( entry_ptr, ¶m ) == E_NOT_ALLOWED )
{
audit_result = RIL_E_RADIO_NOT_AVAILABLE;
break;
}
}
} while (FALSE);
return audit_result;
}
在上面的過程中,要先通過qcril_hash_table_lookup()函式查詢當前的Event,如果沒有找到當前的Request,就認為非法,找到之後,進入qcril_dispatch_event()中派發該Event: IxErrnoType qcril_dispatch_event ( qcril_dispatch_table_entry_type *entry_ptr, qcril_request_params_type *params_ptr) {
if(params_ptr != NULL && (params_ptr->instance_id < QCRIL_MAX_INSTANCE_ID) )
{
// print the recieved date byte stream
qcril_qmi_print_hex(params_ptr->data, params_ptr->datalen);
instance_id = params_ptr->instance_id;
s_ptr = &qcril_state->info[ instance_id ];
modem_id = params_ptr->modem_id;
if (E_SUCCESS == res)
{
//處理當前Request
(entry_ptr->handler)(params_ptr, &ret);
if ( ret.pri_gw_sim_state_changed || ret.pri_cdma_sim_state_changed ||
ret.sec_gw_sim_state_changed || ret.sec_cdma_sim_state_changed ||
ret.ter_gw_sim_state_changed || ret.ter_cdma_sim_state_changed
)
{
qcril_state_transition( instance_id, modem_id, params_ptr->event_id, &ret );
}
}
}
else
{
}
return res;
}
上面的過程通過entry_ptr->handler呼叫當前Event的處理函式。這裡的handler對應qcril_hash_table中的某一項。從上面1.2步驟中我們將qcril_event_table表中的資料拷貝給了qcril_hash_table,所以這裡的handler可以理解為qcril_event_table中的某一項。之後的流程就會進入到某個具體請求的處理函式中,比如對於得到當前SIM卡狀態這個請求,其處理函式為:qcril_uim_request_get_sim_status()。
相關推薦
Qualcomm平臺qcril初始化及訊息處理流程(原)
本節主要來介紹Qcril的初始化流程以及訊息在Qcril中如何傳遞。 Android平臺不同廠商的AP側可以相同,但是Modem側肯定會有很大的差異,RIL層要解決一個問題就是適配不同廠商的Modem,為了達到相容性要求,Android在AP
Qualcomm平臺qcril初始化及訊息處理流程
Android平臺不同廠商的AP側可以相同,但是Modem側肯定會有很大的差異,RIL層要解決一個問題就是適配不同廠商的Modem,為了達到相容性要求,Android在AP與Modem之間搭建了RILC的框架,由不同的Modem廠商將自己的協議連線到AP側。對於高通平臺來說,他的RILC就
Servlet和SpringMVC的初始化及請求處理過程淺析
Servlet是一套Web應用的開發規範,我們按照這套規範編碼就可以實現一個Web應用,使其在Web容器中執行。 我們最開始學習J2EE時,學習和建立的就是Servlet的實現類,後來學習了MVC框架以後,尤其是SpringMVC,就很少直接建立Servlet
SD卡初始化及讀寫流程
SD卡除錯關鍵點: 1. 上電時要延時足夠長的時間給SD卡一個準備過程,在我的程式裡是5秒,根據不同的卡設定不同的延時時間。SD卡初始化第一步在傳送CMD命令之前,在片選有效的情況下首先要傳送至少74個時鐘,否則將有可能出現SD卡不能初始化的問題。 2. SD卡傳送復位命令CMD0
Netty(一):基礎概念及訊息處理流程
1. Netty是什麼? Netty是由JBOSS提供的一個java開源網路通訊框架。Netty可以提供非同步的,非阻塞的,事件驅動的網路應用程式框架和工具,非常適合用來快速開發高效能、高可靠
[PyTorch]PyTorch中模型的參數初始化的幾種方法(轉)
plane alt align frame nor view tps class normal ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~本文目錄1. xavier初始化2. kaiming初始化3. 實際使用中看到的初始化3.1 ResN
Android之訊息處理機制(二)Handler的本質-Message和Looper到底是什麼?
目錄 Android之訊息處理機制(二) 以下皆為乾貨,比較幹,需要讀者細細理解。 前面(一)已經解釋了Handler的基本機制了,下面來概括一下本質。 一、MessageQueue MessageQueue其實就
Android架構分析之Android訊息處理機制(二)
作者:劉昊昱 Android版本:4.4.2 在上一篇文章中我們看了一個使用Handler處理Message訊息的例子,本文我們來分析一下其背後隱藏的Android訊息處理機制。 我們可能比較熟悉Windows作業系統的訊息處理模型: while(GetMessage
Android架構分析之Android訊息處理機制(三)
作者:劉昊昱 Android版本:4.4.2 本文我們來分析AndroidUI執行緒即主執行緒是怎樣實現對訊息的處理的。 UI執行緒的實現類定義在frameworks/base/core/java/android/app/ActivityThread.java檔案中。
初始化與清理之二(清理)
前言 初始化往往是程式設計師呼叫方法前所必須做的事情,但物件用完之後的清理卻常常忽略。對於清理,java中提供了垃圾回收器機制,通常情況下不必由程式設計師關心如何清理的問題;垃圾回收機制在便利的同時也引發兩個問題:一是垃圾回收器只能回收由new分配的記憶體空間
資料歸一化及三種方法(python)
資料標準化(歸一化)處理是資料探勘的一項基礎工作,不同評價指標往往具有不同的量綱和量綱單位,這樣的情況會影響到資料分析的結果,為了消除指標之間的量綱影響,需要進行資料標準化處理,以解決資料指標之間的可比性。原始資料經過資料標準化處理後,各指標處於同一數量級,適合進行綜合對比評價。以下是三種常用的歸一化方法:m
coding git 初始化與專案遠端連線(一)
git 的安裝與配置 一.git簡介 Git是一款免費、開源的分散式版本控制系統。git 的速度很快,對於我們做一些很大的專案來說就方便了很多。 二.git 的下載與安裝 下載地址:https://git-scm.com/download
Netty原始碼分析--初始化Options,新增處理器(四)
接上篇,我們繼續進入AbstractBootstrap類的 initAndRegister() 方法 進入init()方法 設定父級Channel的options, 進入到上節提到的NioServerSocketChannelConfig 其實就是為我們的cha
IPv6實現--傳入包的處理流程(1)
IPv6中資料包的接收處理流程 在一個IPSEC包進入到網路層呼叫~/net/ipv6/ip6_input.c中的ipv6_rcv()函式,然後進入第一個鉤子NF_HOOK(PF_INET6, NF_INET_PRE_ROUTING, skb, dev, NULL,ip6_r
Java內存管理-初始JVM和JVM啟動流程(二)
mac 指令 關系圖 java虛擬機 隔離 hibernate 不同 物理 計算 勿在流沙住高臺,出來混遲早要還的。 做一個積極的人 編碼、改bug、提升自己 我有一個樂園,面向編程,春暖花開! 上一篇分享了什麽是程序,以及Java程序運行的三個階段。也順便
Tomcat 第四篇:請求處理流程(上)
![](https://cdn.geekdigging.com/java/tomcat/tomcat_header.jpg) ## 1. 引言 既然是在講 Tomcat ,那麼一個 HTTP 請求的請求流程是無論如何也繞不開的。 首先拋開所有,使用我們現有的知識面,猜測一下一個請求被 Tomcat 處
Tomcat 第五篇:請求處理流程(下)
![](https://cdn.geekdigging.com/java/tomcat/tomcat_header.jpg) ## 1. 請求處理流程 AprEndPoint 順著上一篇接著聊,當一個請求傳送到 Tomcat 以後,會由聯結器 `Connector` 轉送至 `AprEndPoint` ,
Servlet初始化及處理HTTP請求
png cal 共享 servlet配置 用戶訪問 input 端口號 doget 本地 上一篇詳細介紹了與Servlet相關的幾個核心的接口和類,當我們自己寫Servlet類時,一般需要繼承HttpServlet類,實現init()、doGet()、doP
CentOS7系統環境初始化及安裝的時候網卡改名為eth*
ati cfg iyu ios tcpdump log sysconfig emctl 更新 操作系統安裝: 將網卡名稱設置為eth*,不使用CentOS 7默認的網卡命名規則。所以需要在安裝的時候,增加內核參數。1. 光標選擇“Install CentOS 7”
Web開發中Listener、Filter、Servlet的初始化及調用
children tomcat啟動 什麽 lis exceptio try 部分 OS findchild 我們在使用Spring+SpringMVC開發項目中,web.xml中一般的配置如下: 1 <?xml version="1.0" encoding=