讀書筆記(十一)-定時器和時間管理
系統中有很多與時間相關的程式(比如定期執行的任務,某一時間執行的任務,推遲一段時間執行的任務),因此,時間的管理對於linux來說非常重要.
主要內容:
- 系統時間
- 定時器
- 定時器相關概念
- 定時器執行流程
- 實現程式延遲的方法
- 定時器和延遲的例子
1.系統時間
系統管理的時間有2種:實際時間和定時器.
1.1 實際時間
實際時間就是現實中鐘錶上顯示的時間,其中核心並不常用這個時間,主要是使用者空間的程式有時需要獲取當前時間.
所以核心也管理著這個時間.
實際時間的獲取是在開機後,核心初始化時從RTC讀取的.
核心讀取這個時間後就將其放入核心中的xtime變數中,並且在系統的執行中不斷更新這個值.
注:RTC就是實時時鐘的縮寫,它是用來存放系統時間的裝置.一般和BIOS一樣,是由主機板上的電池供電的,所以即使關機也可以將時間儲存.
實際時間存放的變數xtime在檔案 kernel/time/timekeeping.c中.
/* 按照16位對齊,其實就是2個long型的資料 */
struct timespec xtime __attribute__ ((aligned (16)));
/* timespec結構體的定義如下, 參考 <linux/time.h> */
struct timespec {
__kernel_time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
/* _kernel_time_t 定義如下 */
typedef long __kernel_time_t;
系統讀寫xtime時用的就是順序鎖.
/* 寫入 xtime 參考 do_sometimeofday 方法 */
int do_settimeofday(struct timespec *tv)
{
/* 省略 。。。。 */
write_seqlock_irqsave(&xtime_lock, flags); /* 獲取寫鎖 */
/* 更新 xtime */
write_sequnlock_irqrestore(&xtime_lock, flags); /* 釋放寫鎖 */
/* 省略 。。。。 */
return 0;
}
/* 讀取 xtime 參考 do_gettimeofday 方法 */
void do_gettimeofday(struct timeval *tv)
{
struct timespec now;
getnstimeofday(&now); /* 就是在這個方法中獲取讀鎖,並讀取 xtime */
tv->tv_sec = now.tv_sec;
tv->tv_usec = now.tv_nsec/1000;
}
void getnstimeofday(struct timespec *ts)
{
/* 省略 。。。。 */
/* 順序鎖中讀鎖來迴圈獲取 xtime,直至讀取過程中 xtime 沒有被改變過 */
do {
seq = read_seqbegin(&xtime_lock);
*ts = xtime;
nsecs = timekeeping_get_ns();
/* If arch requires, add in gettimeoffset() */
nsecs += arch_gettimeoffset();
} while (read_seqretry(&xtime_lock, seq));
/* 省略 。。。。 */
}
上述場景中,寫鎖必須要優於讀鎖(因為xtime必須及時更新),而且寫鎖的使用者很少(一般只有系統定期更新xtime的執行緒需要持有這個鎖).
這正是順序鎖的應用場景.
1.2 定時器
定時器是核心中主要使用的時間管理方法,通過定時器,可以有效的排程程式的執行.
動態定時器是核心中使用比較多的定時器,下面重點討論的也是動態定時器.
2.定時器
核心中的定時器有2種,靜態定時器和動態定時器.
靜態定時器一般執行了一些週期性的固定工作:
- 更新系統執行時間
- 更新實際時間
- 在SMP系統上,平衡各個處理器上的執行佇列
- 檢查當前程序是否用盡了自己的時間片,如果用盡,需要重新排程.
- 更新資源消耗和處理器時間統計值
動態定時器顧名思義,是在需要時(一般是推遲程式執行)動態建立的定時器,使用後銷燬(一般都是隻用一次).
一般我們在核心程式碼中使用的定時器基本都是動態定時器,下面重點討論動態定時器相關的概念和使用方法.
3.定時器相關概念
定時器的使用中,下面3個概念非常重要:
- HZ
- jiffies
- 時間中斷處理程式
3.1 HZ
節拍率(HZ)是時鐘中斷的頻率,表示一秒內時鐘中斷的次數.
比如HZ=100 表示一秒內被觸發100次時鐘中斷程式.
HZ的值一般與體系結構有關,x86體系結構一般定義為100,參考檔案 include/asm-generic/param.h
HZ值的大小的設定過程其實就是平衡 精度和效能 的過程,並不是HZ值越高越好.
HZ值 | 優勢 | 劣勢 |
---|---|---|
高HZ | 時鐘中斷程式執行得更加頻繁,依賴時間執行的程式更加精確,對資源消耗和系統執行時間的統計更加精確 | 時鐘中斷執行得頻繁,增加系統負擔,時鐘中斷佔用的CPU時間過多 |
此外,有一點需要注意,核心中使用的HZ可能和使用者空間中定義的HZ值不一致,為了避免使用者空間取得錯誤的時間,核心中也定義了USER_HZ,即使用者空間使用的HZ值.
一般來說,USER_HZ 和 HZ 都是相差整數倍,核心中通過函式 jiffies_to_clock_t 來將核心中的 jiffies 轉化為使用者空間 jiffies.
/* 參見檔案: kernel/time.c *
//*
* Convert jiffies/jiffies_64 to clock_t and back.
*/
clock_t jiffies_to_clock_t(unsigned long x)
{
#if (TICK_NSEC % (NSEC_PER_SEC / USER_HZ)) == 0
# if HZ < USER_HZ
return x * (USER_HZ / HZ);
# else
return x / (HZ / USER_HZ);
# endif
#else
return div_u64((u64)x * TICK_NSEC, NSEC_PER_SEC / USER_HZ);
#endif
}
EXPORT_SYMBOL(jiffies_to_clock_t);
3.2 jiffies
jiffies 用來記錄自系統啟動以來產生的總節拍數.比如系統啟動了N秒,那麼 jiffies 就為 N X HZ
jiffies 的相關定義參考標頭檔案 linux/jiffies.h
/* 64bit和32bit的jiffies定義如下 */
extern u64 __jiffy_data jiffies_64;
extern unsigned long volatile __jiffy_data jiffies;
使用定時器時一般都是以jiffies為單位來延遲程式執行的,比如延遲5個節拍後執行的話,執行時間就是 jiffies + 5
32位的 jiffies 的最大值為 2^32 - 1,在使用時可能會出現迴繞的問題.
比如下面的程式碼:
unsigned long timeout = jiffies + HZ/2; /* 設定超時時間為 0.5秒 */
while (timeout < jiffies)
{
/* 還沒有超時,繼續執行任務 */
}
/* 執行超時後的任務 */
正常情況下,上面的程式碼沒有問題.當 jiffies 接近最大值時,就會出現迴繞問題.
由於是unsigned long 型別,所以 jiffies 達到最大值互會變成0然後再逐漸增加,如下圖所示:
所以在上述的迴圈程式碼中,會出現如下情況:
- 迴圈中第一次比較時,jiffies = J1,沒有超時
- 迴圈中第二次比較時,jiffies = J2,實際已經超時了,但是由於jiffies超過了最大值後又從0開始,所以J2遠遠小於timeout
- while迴圈會執行很長時間(> 2^32-1個節拍)不會結束,幾乎相當於死迴圈了
為了迴避問題,可以使用 linux/jiffies.h 標頭檔案中提供的 time_after, time_before 等巨集
#define time_after(a,b) \
(typecheck(unsigned long, a) && \
typecheck(unsigned long, b) && \
((long)(b) - (long)(a) < 0))
#define time_before(a,b) time_after(b,a)
#define time_after_eq(a,b) \
(typecheck(unsigned long, a) && \
typecheck(unsigned long, b) && \
((long)(a) - (long)(b) >= 0))
#define time_before_eq(a,b) time_after_eq(b,a)
上述程式碼的原理其實就是將unsigned long 型別轉換為 long 型別來避免迴繞帶來的錯誤
long 型別超過最大值時變化趨勢如下:
long型的資料迴繞會出現在 2^31-1 變為 -2^32 的時候,如下圖所示:
- 第一次比較時,jiffies = J1,沒有超時
- 第二次比較時,jiffies = J2,一般J2是負數
理論上(long)timeout - (long)J2 = 正數-負數 = 正數(result)
但是,這個正數(result)一般會大於2^32-1,所以long型的result又發生了一次迴繞,變成了負數.
除非timeout和J2之間的間隔 > 2^32個節拍,result的值才會為正數(注1).
注1: result的值為正數時,必須是在result的值 小於 2^31-1 的情況下,大於2^31-1 會發生迴繞.
上圖中X + Y表示timeout 和 J2 之間經過的節拍數.
result 小於 2^31 - 1.也就是 timeout - J2 < 2^31 - 1
timeout 和 -J2 表示的節拍數如上圖所示.(因為J2是負數,所以-J2表示上圖所示範圍的值)
因為timeout + X + Y - J2 = 2^31-1 + 2^32
所以timeout - J2 < 2^31 - 1 時,X + Y > 2^32
也就是說,當timeout和J2之間至少經過2^32個節拍後,result才可能變為正數.
timeout和J2之間相差這麼多節拍是不可能的(不信可以用HZ將這些節拍換算成秒就知道了….)
利用time_after巨集就可以巧妙地避免迴繞帶來的超時判斷的問題,將之前額的程式碼改成如下程式碼即可:
unsigned long timeout = jiffies + HZ/2; /* 設定超時時間為 0.5秒 */
while (time_after(jiffies, timeout))
{
/* 還沒有超時,繼續執行任務 */
}
/* 執行超時後的任務 */
3.3 時鐘中斷處理程式
時鐘中斷處理程式作為系統定時器而註冊到核心中,體系結構的不同,可能時鐘中斷處理程式中處理的內容不同.
但是以下這些基本的工作都會執行:
- 獲得xtime_lock 鎖,以便對訪問 jiffies_64 和 牆上時間 xtime 進行保護
- 需要時應答或重新設定系統時鐘
- 週期性地使用牆上時間更新實時時鐘
- 呼叫 tick_periodlc()
tick_periodlc函式位於: kernel/time/tick-commom.c 中
static void tick_periodic(int cpu)
{
if (tick_do_timer_cpu == cpu) {
write_seqlock(&xtime_lock);
/* Keep track of the next tick event */
tick_next_period = ktime_add(tick_next_period, tick_period);
do_timer(1);
write_sequnlock(&xtime_lock);
}
update_process_times(user_mode(get_irq_regs()));
profile_tick(CPU_PROFILING);
}
其中最重要的是 do_timer 和 update_process_times 函式
我瞭解的步驟進行了簡單的註釋.
void do_timer(unsigned long ticks)
{
/* jiffies_64 增加指定ticks */
jiffies_64 += ticks;
/* 更新實際時間 */
update_wall_time();
/* 更新系統的平均負載值 */
calc_global_load();
}
void update_process_times(int user_tick)
{
struct task_struct *p = current;
int cpu = smp_processor_id();
/* 更新當前程序佔用CPU的時間 */
account_process_tick(p, user_tick);
/* 同時觸發軟中斷,處理所有到期的定時器 */
run_local_timers();
rcu_check_callbacks(cpu, user_tick);
printk_tick();
/* 減少當前程序的時間片數 */
scheduler_tick();
run_posix_cpu_timers(p);
}
4.定時器執行流程
這裡討論的定時器執行流程是動態定時器的執行流程.
4.1 定時器的定義
定時器在核心中用一個連結串列來儲存,連結串列的每個節點都是一個定時器.
參見標頭檔案 linux/timer.h
struct timer_list {
struct list_head entry;
unsigned long expires;
void (*function)(unsigned long);
unsigned long data;
struct tvec_base *base;
#ifdef CONFIG_TIMER_STATS
void *start_site;
char start_comm[16];
int start_pid;
#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
通過加入條件編譯的引數,可以追加一些除錯資訊.
4.2 定時器的生命週期
一個動態定時器的生命週期中,一般會經過下面的幾個步驟:
1.初始化定時器
struct timer_list my_timer; /* 定義定時器 */
init_timer(&my_timer); /* 初始化定時器 */
2.填充定時器
my_timer.expires = jiffies + delay; /* 定義超時的節拍數 */
my_timer.data = 0; /* 給定時器函式傳入的引數 */
my_timer.function = my_function; /* 定時器超時時,執行的自定義函式 */
/* 從定時器結構體中,我們可以看出這個函式的原型應該如下所示: */
void my_function(unsigned long data);
3.啟用定時器和修改定時器
啟用定時器之後才會被觸發,否則定時器不會執行.
修改定時器主要是修改定時器的延遲時間,修改定時器後,不管原型定時器有沒有被啟用,都會處於啟用狀態.
填充定時器結構之後,可以只啟用定時器,也可以只修改定時器,也可以啟用定時器後再修改定時器.
所以填充定時器結構和觸發定時器之間的步驟,也就是虛線框中的步驟是不確定的.
add_timer(&my_timer); /* 啟用定時器 */
mod_timer(&my_timer, jiffies + new_delay); /* 修改定時器,設定新的延遲時間
4.觸發定時器
每次時鐘中斷處理程式會檢查已經啟用的定時器是否超時,如果超時就執行定時器結構中的自定義函式.
5.刪除定時器
啟用和未被啟用的定時器都可以被刪除,已經超時的定時器會被自動刪除,不用特意去刪除.
/*
* 刪除啟用的定時器時,此函式返回1
* 刪除未啟用的定時器時,此函式返回0
*/
del_timer(&my_timer);
在多核處理器上用del_timer函式刪除定時器時,可能在刪除時正好另一個CPU核上的時鐘中斷處理程式正在執行這個定時器,於是就形成了競爭條件.
為了避免競爭條件,建議使用 del_timer_sync 函式來刪除定時器.
del_timer_sync 函式會等待其他處理器上的定時器處理程式全部結束後,才刪除指定的定時器.
/*
* 和del_timer 不同,del_timer_sync 不能在中斷上下文中執行
*/
del_timer_sync(&my_timer);
5.實現程式延遲的方法
核心中有個利用定時器實現延遲的函式 schedule_timeout
這個函式會將當前的任務睡眠到指定時間後喚醒,所以等待時不會佔用cpu時間
/* 將任務設定為可中斷睡眠狀態 */
set_current_state(TASK_INTERRUPTIBLE);
/* 小睡一會兒,“s“秒後喚醒 */
schedule_timeout(s*HZ);
檢視schedule_timeout 函式的實現方法,可以看出是如何使用定時器的.
signed long __sched schedule_timeout(signed long timeout)
{
/* 定義一個定時器 */
struct timer_list timer;
unsigned long expire;
switch (timeout)
{
case MAX_SCHEDULE_TIMEOUT:
/*
* These two special cases are useful to be comfortable
* in the caller. Nothing more. We could take
* MAX_SCHEDULE_TIMEOUT from one of the negative value
* but I' d like to return a valid offset (>=0) to allow
* the caller to do everything it want with the retval.
*/
schedule();
goto out;
default:
/*
* Another bit of PARANOID. Note that the retval will be
* 0 since no piece of kernel is supposed to do a check
* for a negative retval of schedule_timeout() (since it
* should never happens anyway). You just have the printk()
* that will tell you if something is gone wrong and where.
*/
if (timeout < 0) {
printk(KERN_ERR "schedule_timeout: wrong timeout "
"value %lx\n", timeout);
dump_stack();
current->state = TASK_RUNNING;
goto out;
}
}
/* 設定超時時間 */
expire = timeout + jiffies;
/* 初始化定時器,超時處理函式是 process_timeout,後面再補充說明一下這個函式 */
setup_timer_on_stack(&timer, process_timeout, (unsigned long)current);
/* 修改定時器,同時會啟用定時器 */
__mod_timer(&timer, expire, false, TIMER_NOT_PINNED);
/* 將本任務睡眠,排程其他任務 */
schedule();
/* 刪除定時器,其實就是 del_timer_sync 的巨集
del_singleshot_timer_sync(&timer);
/* Remove the timer from the object tracker */
destroy_timer_on_stack(&timer);
timeout = expire - jiffies;
out:
return timeout < 0 ? 0 : timeout;
}
EXPORT_SYMBOL(schedule_timeout);
/*
* 超時處理函式 process_timeout 裡面只有一步操作,喚醒當前任務。
* process_timeout 的引數其實就是 當前任務的地址
*/
static void process_timeout(unsigned long __data)
{
wake_up_process((struct task_struct *)__data);
}
schedule_timeout 一般用於延遲時間較長的程式.
這裡的延遲時間較長是對於計算機而言的,其實也就是延遲大於1個節拍(jiffies).
對於某些及其短暫的延遲,比如只有1ms,甚至1us,1ns的延遲,必須使用特殊的延遲方法.
1s = 1000ms = 1000000us = 1000000000ns (1秒=1000毫秒=1000000微秒=1000000000納秒)
假設HZ=100,那麼1個節拍的時間間隔是1/100秒,大概10ms左右.
所以對於那些極其短暫的延遲,schedule_timeout 函式是無法使用的.
好在核心對於這些短暫,精確的延遲要求也提供了相應的巨集.
/* 具體實現參見 include/linux/delay.h
* 以及 arch/x86/include/asm/delay.h
*/
#define mdelay(n) ...
#define udelay(n) ...
#define ndelay(n) ...
通過這些巨集,可以簡單地實現延遲,比如延遲5ns,只需 ndelay(5)即可.
這些短延遲的實現原理並不複雜
首先,核心在啟動時就計算出了當前處理器1秒能執行多少此迴圈,即loops_per_jiffy
(loops_per_jiffy的計算方法參見 init/main.c檔案中的 callbrate_delay方法)
然後算出延遲5ns需要迴圈多少次,執行那麼多次空迴圈即可達到延遲的效果.
loops_per_jiffy的值可以在啟動資訊中看到:
[[email protected] ~]# dmesg | grep delay
Calibrating delay loop (skipped), value calculated using timer frequency.. 6387.58 BogoMIPS (lpj=3193792)
我的虛擬機器中看到(lpj=3193792)
6.定時器和延遲的例子
下面的例子測試了短延遲,自定義定時器以及 schedule_timeout 的使用
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <asm/param.h>
#include <linux/delay.h>
#include "kn_common.h"
MODULE_LICENSE("Dual BSD/GPL");
static void test_short_delay(void);
static void test_delay(void);
static void test_schedule_timeout(void);
static void my_delay_function(unsigned long);
static int testdelay_init(void)
{
printk(KERN_ALERT "HZ in current system: %dHz\n", HZ);
/* test short delay */
test_short_delay();
/* test delay */
test_delay();
/* test schedule timeout */
test_schedule_timeout();
return 0;
}
static void testdelay_exit(void)
{
printk(KERN_ALERT "*************************\n");
print_current_time(0);
printk(KERN_ALERT "testdelay is exited!\n");
printk(KERN_ALERT "*************************\n");
}
static void test_short_delay()
{
printk(KERN_ALERT "jiffies [b e f o r e] short delay: %lu", jiffies);
ndelay(5);
printk(KERN_ALERT "jiffies [a f t e r] short delay: %lu", jiffies);
}
static void test_delay()
{
/* 初始化定時器 */
struct timer_list my_timer;
init_timer(&my_timer);
/* 填充定時器 */
my_timer.expires = jiffies + 1*HZ; /* 2秒後超時函式執行 */
my_timer.data = jiffies;
my_timer.function = my_delay_function;
/* 啟用定時器 */
add_timer(&my_timer);
}
static void my_delay_function(unsigned long data)
{
printk(KERN_ALERT "This is my delay function start......\n");
printk(KERN_ALERT "The jiffies when init timer: %lu\n", data);
printk(KERN_ALERT "The jiffies when timer is running: %lu\n", jiffies);
printk(KERN_ALERT "This is my delay function end........\n");
}
static void test_schedule_timeout()
{
printk(KERN_ALERT "This sample start at : %lu", jiffies);
/* 睡眠2秒 */
set_current_state(TASK_INTERRUPTIBLE);
printk(KERN_ALERT "sleep 2s ....\n");
schedule_timeout(2*HZ);
printk(KERN_ALERT "This sample end at : %lu", jiffies);
}
module_init(testdelay_init);
module_exit(testdelay_exit);
其中用到的 kn_common.h 和 kn_common.c 參見之前的部落格 《Linux核心設計與實現》讀書筆記(六)- 核心資料結構
Makefile如下:
# must complile on customize kernel
obj-m += mydelay.o
mydelay-objs := testdelay.o kn_common.o
#generate the path
CURRENT_PATH:=$(shell pwd)
#the current kernel version number
LINUX_KERNEL:=$(shell uname -r)
#the absolute path
LINUX_KERNEL_PATH:=/usr/src/kernels/$(LINUX_KERNEL)
#complie object
all:
make -C $(LINUX_KERNEL_PATH) M=$(CURRENT_PATH) modules
rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c .tmp_versions *.unsigned
#clean
clean:
rm -rf modules.order Module.symvers .*.cmd *.o *.mod.c *.ko .tmp_versions *.unsigned
執行測試命令及檢視結果的方法如下:(我的測試系統是 CentOS 6.3 x64)
[[email protected] chap11]# make
[[email protected] chap11]# insmod mydelay.ko
[[email protected] chap11]# rmmod mydelay.ko
[[email protected] chap11]# dmesg | tail -14
HZ in current system: 1000Hz
jiffies [b e f o r e] short delay: 4296079617
jiffies [a f t e r] short delay: 4296079617
This sample start at : 4296079619
sleep 2s ....
This is my delay function start......
The jiffies when init timer: 4296079619
The jiffies when timer is running: 4296080621
This is my delay function end........
This sample end at : 4296081622
*************************
2013-5-9 23:7:20
testdelay is exited!
*************************
結果說明:
1.短延遲只延遲了5ns,所以執行前後的 jiffies 是一樣的
jiffies [b e f o r e] short delay: 4296079617
jiffies [a f t e r] short delay: 4296079617
2.自定義定時器延遲了1秒後執行自定義函式,由於我的系統HZ=1000,所以jiffies應該相差1000
The jiffies when init timer: 4296079619
The jiffies when timer is running: 4296080621
實際上jiffies相差了1002,多了2個節拍
3.schedule_timeout 延遲了2秒,jiffies 應相差2000
This sample start at : 4296079619
This sample end at : 4296081622
實際上 jiffies 相差了2003,多了3個節拍
以上結果也說明了定時器的延遲並不是那麼精確,差個2,3個節拍其實就是誤差2,3毫秒(因為HZ=1000)
如果HZ=100的話,一個節拍是10毫秒,那麼定時器的誤差可能就發現不了(誤差只有2,3毫秒,沒有超過1個節拍).
相關推薦
讀書筆記(十一)-定時器和時間管理
系統中有很多與時間相關的程式(比如定期執行的任務,某一時間執行的任務,推遲一段時間執行的任務),因此,時間的管理對於linux來說非常重要. 主要內容: 系統時間 定時器 定時器相關概念 定時器執行流程 實現程式延遲的方法 定時器和延遲的例子 1.系
圖解設計模式讀書筆記(十一)——Chain of Responsibility(責任鏈)模式
看到這個模式,最先想到的就是okhttp跟Android的事件處理機制了。 使用場景: 當功能出現分層,層級之間相對獨立這種情況,可考慮使用責任鏈模式。 類關係圖: Handler是一個抽象類,它擁有屬性next,型別是自身型別,並有處理請求但request方法。
SQL讀書筆記(十一)更新和刪除資料
筆記參考來自SQL必知必會,摘抄下書中的一些關鍵方便以後自己查詢 更新和刪除都是比較敏感的操作,因為沒辦法撤銷,所以修改時要注意,更新主要分為更新特定行和所有行。所以要十分注意避免更高錯誤 不要省略WHERE子句 在使用UPDATE時一定要細心。因為稍不注意
Python學習筆記(十一)關鍵字、函式和方法
關鍵字是 Python內建的、具有特殊意義的識別符號 關鍵字後面不需要使用括號 函式封裝了獨立功能,可以直接呼叫
Java筆記(十一)通用容器類和總結
結構 翻轉 隨機 抽象 clear out 內部類 依賴 keys 通用容器類和總結 一、抽象容器類 一)AbstractCollection 提供了Collection接口的基礎實現,具體來說,實現了如下方法: public boolean addAll(Collec
《Linux核心設計與實現》讀書筆記(十六)- 頁快取記憶體和頁回寫
好久沒有更新了。。。 主要內容: 快取簡介 頁快取記憶體 頁回寫 1. 快取簡介 在程式設計中,快取是很常見也很有效的一種提高程式效能的機制。 linux核心也不例外,為了提高I/O效能,也引入了快取機制,即將一部分磁碟上的資料快取到記憶體中。 1.1 原理 之所以通過快取能
莫煩pytorch學習筆記(十一)——Optimizer優化器
建造第一個神經網路——Optimizer優化器 要點 這節內容主要是用 Torch 實踐,中起到的幾種優化器。 下圖就是這節內容對比各種優化器的效果: 偽資料 為了對比各種優化器的效果, 我們需要有一些資料, 今天我們還是自己編一些偽資料, 這批資料是這樣的
Nodejs學習筆記(十一)--- 資料採集器示例(request和cheerio)
目錄 寫在之前 很多人都有做資料採集的需求,用不同的語言,不同的方式都能實現,我以前也用C#寫過,主要還是傳送各類請求和正則解析資料比較繁瑣些,總體來說沒啥不好的,就是效率要差一些, 用nodejs寫採集程式還是比較有效率(可能也只是相對C#來說),今天主要用一個示例來說一下使用node
C#學習筆記(十六):索引器和重載運算符
instance cit png form mage 創建 return position args 二維數組如何映射到一維數組 重載運算符 1、算術運算符 2、關系運算符, < 和 > 成對重載 using System; using Sy
《TCP/IP具體解釋》讀書筆記(21章)-TCP的超時與重傳
打開 定時器 是否 檢查 例如 技術 blog 信息 全部 TCP提供可靠的運輸層。它使用的方法之中的一個就是確認從還有一端收到的數據。但數據和確認都有可能會丟失。TCP通過在發送時設置一個定時器來解決這樣的問題。假設當定時器溢出時還沒有收到確認,它就重傳該數據。對於實現
《TCP/IP具體解釋》讀書筆記(19章)-TCP的交互數據流
font alt 算法 方向 它的 字節 隨機 收集 計算 在TCP進行傳輸數據時。能夠分為成塊數據流和交互數據流兩種。假設按字節計算。成塊數據與交互數據的比例約為90%和10%,TCP須要同一時候處理這兩類數據,且處理的算法不同。書籍本章中以Rlogin應用為例觀察交
Python學習筆記(十一)裝飾器
before 原來 return wrap 文本 wiki 模塊 http 學習筆記 摘抄:https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014318
如鵬網學習筆記(十一)JQuery
on() jquery操作 等於 asc 空字符串 name屬性 string 包含 ret 一、jQuery簡介 jQuery是一個JavaScript庫,特性豐富,包含若幹對象和很多函數,可以替代傳統DOM編程的操作方式和操作風格 jQuery通過對DOM
《Linux內核設計與實現》讀書筆記(十二)- 內存管理
enable vmalloc 緩沖 turn lean png border 編譯 不一致 內核的內存使用不像用戶空間那樣隨意,內核的內存出現錯誤時也只有靠自己來解決(用戶空間的內存錯誤可以拋給內核來解決)。 所有內核的內存管理必須要簡潔而且高效。 主要內容: 內
《Linux內核設計與實現》讀書筆記(十六)- 頁高速緩存和頁回寫
第一次 源碼 進行 lose 減少 文件緩存 掩碼 recycle 創建 主要內容: 緩存簡介 頁高速緩存 頁回寫 1. 緩存簡介 在編程中,緩存是很常見也很有效的一種提高程序性能的機制。 linux內核也不例外,為了提高I/O性能,也引入了緩存機
Python學習筆記(十一)
def __init__ 實現 完成 cti uniq ive 枚舉 elf 一、Python的多重繼承功能 Python中的主線是單一繼承的 Python中可以存在功能類,即專註於完成一定功能的類,相當於其他一些動態語言中的接口的概念 class Class_
EF學習筆記(十一):實施繼承
long cannot oid data- turn cati com list pac 學習總目錄:ASP.NET MVC5 及 EF6 學習筆記 - (目錄整理) 上篇鏈接:EF學習筆記(十) 處理並發 本篇原文鏈接:Implementing Inheritance 面
R語言學習筆記(十一):廣義線性模型
學習筆記 Education 5.0 1.3 style only 可能性 div erro #Logistic 回歸 install.packages("AER") data(Affairs,package="AER") summary(Affairs) a
python學習筆記(十一)之函數
last 函數返回 traceback keep disco show 全局變量 not 默認參數 牛刀小試: 定義一個無參函數 1 >>> def myFirstFunc(): 2 ... print("Hello python
JavaScript學習筆記(十一)——閉包
進行 性能 ole 直接 狀態 聲明變量 垃圾 函數 官方網站 在學習廖雪峰前輩的JavaScript教程中,遇到了一些需要註意的點,因此作為學習筆記列出來,提醒自己註意! 如果大家有需要,歡迎訪問前輩的博客https://www.liaoxuefeng.com/學習。