1. 程式人生 > >Linux核心模組程式設計與核心模組LICENSE -《詳解(第3版)》預讀

Linux核心模組程式設計與核心模組LICENSE -《詳解(第3版)》預讀

Linux核心模組簡介

Linux核心的整體結構已經非常龐大,而其包含的元件也非常多。我們怎樣把需要的部分都包含在核心中呢?
一種方法是把所有需要的功能都編譯到Linux核心。這會導致兩個問題,一是生成的核心會很大,二是如果我們要在現有的核心中新增或刪除功能,將不得不重新編譯核心。
有沒有一種機制使得編譯出的核心本身並不需要包含所有功能,而在這些功能需要被使用的時候,其對應的程式碼被動態地載入到核心中呢?
Linux提供了這樣的一種機制,這種機制被稱為模組(Module)。模組具有這樣的特點。
  1. 模組本身不被編譯入核心映像,從而控制了核心的大小。
  2. 模組一旦被載入,它就和核心中的其他部分完全一樣。
為了使讀者建立對模組的初步感性認識,我們先來看一個最簡單的核心模組“Hello World”,如程式碼清單4.1所示。
程式碼清單4.1  一個最簡單的Linux核心模組
01 /*
02  * a simple kernel module: hello
03  *
04  * Copyright (C) 2014 Barry Song  (
[email protected]
) 05 * 06 * Licensed under GPLv2 or later. 07 */ 08 09 #include <linux/init.h> 10 #include <linux/module.h> 11 12 static int __init hello_init(void) 13 { 14 printk(KERN_INFO "Hello World enter\n"); 15 return 0; 16 } 17 module_init(hello_init); 18 19 static void __exit hello_exit(void) 20 { 21 printk(KERN_INFO "Hello World exit\n "); 22 } 23 module_exit(hello_exit); 24 25 MODULE_AUTHOR("Barry Song <
[email protected]
>"); 26 MODULE_LICENSE("GPL v2"); 27 MODULE_DESCRIPTION("A simple Hello World Module"); 28 MODULE_ALIAS("a simplest module");

這個最簡單的核心模組只包含核心模組載入函式、解除安裝函式和對GPL v2許可許可權的宣告以及一些描述資訊,位於本書配套原始碼的/kernel/drivers/hello目錄。編譯它會產生hello.ko目標檔案,通過“insmod ./hello.ko”命令可以載入它,通過“rmmod hello”命令可以解除安裝它,載入時輸出“Hello World enter”,解除安裝時輸出“Hello World exit”。
核心模組中用於輸出的函式是核心空間的printk()而非使用者空間的printf(),printk()的用法和printf()基本相似,但前者可定義輸出級別。printk()可作為一種最基本的核心除錯手段,在Linux驅動的除錯章節中將詳細講解這個函式。
在Linux中,使用lsmod命令可以獲得系統中載入了的所有模組以及模組間的依賴關係,例如:
Module                  Size  Used by
hello                   9 472  0
nls_iso8859_1          12 032  1
nls_cp437              13 696  1
vfat                   18 816  1
fat                    57 376  1 vfat
...

lsmod命令實際上讀取並分析“/proc/modules”檔案,與上述lsmod命令結果對應的“/proc/modules”檔案如下:
$ cat /proc/modules
hello 12393 0 - Live 0xe67a2000 (OF)
nls_utf8 12493 1 - Live 0xe678e000
isofs 39596 1 - Live 0xe677f000
vboxsf 42561 2 - Live 0xe6767000 (OF)
...
核心中已載入模組的資訊也存在於/sys/module目錄下,載入hello.ko後,核心中將包含/sys/module/hello目錄,該目錄下又包含一個refcnt檔案和一個sections目錄,在/sys/module/hello目錄下執行“tree –a”得到如下目錄樹:
[email protected]:/sys/module/hello# tree -a
.
├── coresize
├── holders
├── initsize
├── initstate
├── notes
│   └── .note.gnu.build-id
├── refcnt
├── sections
│   ├── .exit.text
│   ├── .gnu.linkonce.this_module
│   ├── .init.text
│   ├── .note.gnu.build-id
│   ├── .rodata.str1.1
│   ├── .strtab
│   └── .symtab
├── srcversion
├── taint
└── uevent

3 directories, 15 files

modprobe命令比insmod命令要強大,它在載入某模組時,會同時載入該模組所依賴的其他模組。使用modprobe命令載入的模組若以“modprobe -r filename”的方式解除安裝將同時解除安裝其依賴的模組。模組之間的依賴關係上存放在根檔案系統的/lib/modules/<kernel-version>/modules.dep檔案中,實際上是在整體編譯核心的時候由depmod工具生成的,它的格式非常簡單:
kernel/lib/cpu-notifier-error-inject.ko: kernel/lib/notifier-error-inject.ko
kernel/lib/pm-notifier-error-inject.ko: kernel/lib/notifier-error-inject.ko
kernel/lib/lru_cache.ko:
kernel/lib/cordic.ko:
kernel/lib/rbtree_test.ko:
kernel/lib/interval_tree_test.ko:
updates/dkms/vboxvideo.ko: kernel/drivers/gpu/drm/drm.ko

使用modinfo <模組名>命令可以獲得模組的資訊,包括模組作者、模組的說明、模組所支援的引數以及vermagic:
# modinfo hello.ko
filename:       hello.ko
alias:          a simplest module
description:    A simple Hello World Module
license:        GPL v2
author:         Barry Song <[email protected]>
srcversion:     081230411494509792BD4A3
depends:        
vermagic:       3.8.0-39-generic SMP mod_unload modversions 686

Linux核心模組程式結構

一個Linux核心模組主要由如下幾個部分組成。
(1)模組載入函式
當通過insmod或modprobe命令載入核心模組時,模組的載入函式會自動被核心執行,完成本模組的相關初始化工作。
(2)模組解除安裝函式
當通過rmmod命令解除安裝某模組時,模組的解除安裝函式會自動被核心執行,完成與模組解除安裝函式相反的功能。
(3)模組許可證宣告
許可證(LICENSE)宣告描述核心模組的許可許可權,如果不宣告LICENSE,模組被載入時,將收到核心被汙染 (kernel tainted)的警告。
在Linux核心模組領域,可接受的LICENSE包括“GPL”、“GPL v2”、“GPL and additional rights”、“Dual BSD/GPL”、“Dual MPL/GPL”和“Proprietary”(關於模組是否可以採用非GPL許可權如“Proprietary”,這個在學術界和法律界都有爭議)。
大多數情況下,核心模組應遵循GPL相容許可權。Linux核心模組最常見的是以MODULE_LICENSE( "GPL v2" )語句宣告模組採用GPL v2。
(4)模組引數(可選)。
模組引數是模組被載入的時候可以被傳遞給它的值,它本身對應模組內部的全域性變數。
(5)模組匯出符號(可選)。
核心模組可以匯出符號(symbol,對應於函式或變數),這樣其他模組可以使用本模組中的變數或函式。
(6)模組作者等資訊宣告(可選)。

 模組載入函式

Linux核心模組載入函式一般以_ _init標識宣告,典型的模組載入函式的形式如程式碼清單4.2所示。
程式碼清單4.2  核心模組載入函式
1     static int _ _init initialization_function(void)
2     {    
3         /* 初始化程式碼 */
4     }
5     module_init(initialization_function);

模組載入函式則以“module_init(函式名)”的形式被指定。它返回整型值,若初始化成功,應返回0。而在初始化失敗時,應該返回錯誤編碼。在Linux核心裡,錯誤編碼是一個接近於0的負值,在<linux/errno.h>中定義,包含-ENODEV、-ENOMEM之類的符號值。總是返回相應的錯誤編碼是種非常好的習慣,因為只有這樣,使用者程式才可以利用perror等方法把它們轉換成有意義的錯誤資訊字串。
在Linux核心中,可以使用request_module(const char *fmt, …)函式載入核心模組,驅動開發人員可以通過呼叫
request_module(module_name);
這種靈活的方式載入其他核心模組。
在Linux中,所有標識為_ _init的函式如果直接編譯進入核心,成為核心映象的一部分,在連線的時候都放在.init.text這個區段內。
#define _ _init        _ _attribute_ _ ((_ _section_ _ (".init.text")))
所有的_ _init函式在區段.initcall.init中還儲存了一份函式指標,在初始化時核心會通過這些函式指標呼叫這些_ _init函式,並在初始化完成後,釋放init區段(包括.init.text、.initcall.init等)的記憶體。
除了函式以外,資料也可以被定義為_ _initdata,對於只是初始化階段需要的資料,核心在初始化完後,也可以釋放它們佔用的記憶體。例如,下面的程式碼中將hello_data定義為__initdata。
static int hello_data __initdata = 1;

static int __init hello_init(void)
{
    printk(KERN_INFO "Hello, world %d\n", hello_data);
    return 0;
}
module_init(hello_init);

static void __exit hello_exit(void)
{
    printk(KERN_INFO "Goodbye, world\n");
}
module_exit(hello_exit);

模組解除安裝函式


Linux核心模組載入函式一般以_ _exit標識宣告,典型的模組解除安裝函式的形式如程式碼清單4.3所示。
程式碼清單4.3  核心模組解除安裝函式
1    static void _ _exit cleanup_function(void)
2    {
3          /* 釋放程式碼 */
4    }
5    module_exit(cleanup_function);

模組解除安裝函式在模組解除安裝的時候執行,不返回任何值,必須以“module_exit(函式名)”的形式來指定。通常來說,模組解除安裝函式要完成與模組載入函式相反的功能。
我們用__exit來修飾模組解除安裝函式,可以告訴核心如果相關的模組被直接編譯進核心(即built-in),則cleanup_function() 函式會被省略直接不連線進最後的映象。既然模組被built-in了,就不可能解除安裝它了,解除安裝函式也就沒有存在的必要了。除了函式以外,只是退出階段採用的資料也可以用__exitdata來形容。

模組引數


我們可以用“module_param(引數名,引數型別,引數讀/寫許可權)”為模組定義一個引數,例如下列程式碼定義了1個整型引數和1個字元指標引數:
static char *book_name = "dissecting Linux Device Driver";
module_param(book_name, charp, S_IRUGO);

static int book_num = 4000;
module_param(book_num, int, S_IRUGO);

在裝載核心模組時,使用者可以向模組傳遞引數,形式為“insmode(或modprobe)模組名 引數名=引數值”,如果不傳遞,引數將使用模組內定義的預設值。如果模組被built-in,就無法insmod了,但是bootloader可以通過在bootargs裡設定“模組名.引數名=值”的形式給該built-in的模組傳遞引數。
引數型別可以是byte、short、ushort、int、uint、long、ulong、charp(字元指標)、bool或invbool(布林的反),在模組被編譯時會將module_param中宣告的型別與變數定義的型別進行比較,判斷是否一致。
除此之外,模組也可以擁有引數陣列,形式為“module_param_array(陣列名,陣列型別,陣列長,引數讀/寫許可權)”。
模組被載入後,在/sys/module/目錄下將出現以此模組名命名的目錄。當“引數讀/寫許可權”為0時,表示此引數不存在sysfs檔案系統下對應的檔案節點,如果此模組存在“引數讀/寫許可權”不為0的命令列引數,在此模組的目錄下還將出現parameters目錄,包含一系列以引數名命名的檔案節點,這些檔案的許可權值就是傳入module_param()的“引數讀/寫許可權”,而檔案的內容為引數的值。
執行insmod或modprobe命令時,應使用逗號分隔輸入的陣列元素。
現在我們定義一個包含兩個引數的模組(如程式碼清單4.4,位於本書原始碼/kernel/drivers/param目錄),並觀察模組載入時被傳遞引數和不傳遞引數時的輸出。
程式碼清單4.4  帶引數的核心模組
01 #include <linux/init.h>
02 #include <linux/module.h>
03
04 static char *book_name = "dissecting Linux Device Driver";
05 module_param(book_name, charp, S_IRUGO);
06
07 static int book_num = 4000;
08 module_param(book_num, int, S_IRUGO);
09
10 static int __init book_init(void)
11 {
12     printk(KERN_INFO "book name:%s\n", book_name);
13     printk(KERN_INFO "book num:%d\n", book_num);
14     return 0;
15 }
16 module_init(book_init);
17
18 static void __exit book_exit(void)
19 {
20     printk(KERN_INFO "book module exit\n ");
21 }
22 module_exit(book_exit);
23
24 MODULE_AUTHOR("Barry Song <[email protected]>");
25 MODULE_LICENSE("GPL v2");
26 MODULE_DESCRIPTION("A simple Module for testing module params");
27 MODULE_VERSION("V1.0");

對上述模組執行“insmod book.ko”命令載入,相應輸出都為模組內的預設值,通過檢視“/var/log/messages”日誌檔案可以看到核心的輸出:
# tail -n 2 /var/log/messages
Jul  2 01:03:10 localhost kernel:  <6> book name:dissecting Linux Device Driver
Jul  2 01:03:10 localhost kernel:  book num:4000

當用戶執行“insmod book.ko book_name='GoodBook' book_num=5000”命令時,輸出的是使用者傳遞的引數:
# tail -n 2 /var/log/messages
Jul  2 01:06:21 localhost kernel:  <6> book name:GoodBook
Jul  2 01:06:21 localhost kernel:  book num:5000
Jul  2 01:06:21 localhost kernel:  book num:5000
另外,在/sys目錄下,也可以看到book模組的引數:
[email protected]:/sys/module/book/parameters$ tree
.
├── book_name
└── book_num

匯出符號

Linux的“/proc/kallsyms”檔案對應著核心符號表,它記錄了符號以及符號所在的記憶體地址。
模組可以使用如下巨集匯出符號到核心符號表:
EXPORT_SYMBOL(符號名);
EXPORT_SYMBOL_GPL(符號名);
    匯出的符號將可以被其他模組使用,使用前宣告一下即可。EXPORT_SYMBOL_GPL()只適用於包含GPL許可權的模組。程式碼清單4.5給出了一個匯出整數加、減運算函式符號的核心模組的例子。
程式碼清單4.5  核心模組中的符號匯出
01 #include <linux/init.h>
02 #include <linux/module.h>
03
04 int add_integar(int a, int b)
05 {
06     return a + b;
07 }
08 EXPORT_SYMBOL_GPL(add_integar);
09
10 int sub_integar(int a, int b)
11 {
12     return a - b;
13 }
14 EXPORT_SYMBOL_GPL(sub_integar);
15
16 MODULE_LICENSE("GPL v2");

從“/proc/kallsyms”檔案中找出add_integar、sub_integar相關資訊:
# grep integar /proc/kallsyms
e679402c r __ksymtab_sub_integar    [export_symb]
e679403c r __kstrtab_sub_integar    [export_symb]
e6794038 r __kcrctab_sub_integar    [export_symb]
e6794024 r __ksymtab_add_integar    [export_symb]
e6794048 r __kstrtab_add_integar    [export_symb]
e6794034 r __kcrctab_add_integar    [export_symb]
e6793000 t add_integar    [export_symb]
e6793010 t sub_integar    [export_symb]

模組宣告與描述


在Linux核心模組中,我們可以用MODULE_AUTHOR、MODULE_DESCRIPTION、MODULE_VERSION、MODULE_DEVICE_TABLE、MODULE_ALIAS分別宣告模組的作者、描述、版本、裝置表和別名,例如:
MODULE_AUTHOR(author);
MODULE_DESCRIPTION(description);
MODULE_VERSION(version_string);
MODULE_DEVICE_TABLE(table_info);
MODULE_ALIAS(alternate_name);

對於USB、PCI等裝置驅動,通常會建立一個MODULE_DEVICE_TABLE,表明該驅動模組所支援的裝置,如程式碼清單4.6所示。
程式碼清單4.6  驅動所支援的裝置列表
1 /* 對應此驅動的裝置表 */
2 static struct usb_device_id skel_table [] = {
3 { USB_DEVICE(USB_SKEL_VENDOR_ID,
4       USB_SKEL_PRODUCT_ID) },
5     { } /* 表結束 */
6 };
7
8 MODULE_DEVICE_TABLE (usb, skel_table);

此時,並不需要讀者理解MODULE_DEVICE_TABLE的作用,後續相關章節會有詳細介紹。

 模組的使用計數


Linux 2.4核心中,模組自身通過MOD_INC_USE_COUNT、MOD_DEC_USE_COUNT巨集來管理自己被使用的計數。
Linux 2.6以後的核心提供了模組計數管理介面try_module_get(&module)和module_put (&module),從而取代Linux 2.4核心中的模組使用計數管理巨集。模組的使用計數一般不必由模組自身管理,而且模組計數管理還考慮了SMP與PREEMPT機制的影響。

int try_module_get(struct module *module);

該函式用於增加模組使用計數;若返回為0,表示呼叫失敗,希望使用的模組沒有被載入或正在被解除安裝中。

void module_put(struct module *module);

該函式用於減少模組使用計數。
try_module_get ()與module_put()的引入與使用與Linux 2.6以後的核心下的裝置模型密切相關。Linux 2.6以後的核心為不同型別的裝置定義了struct module *owner域,用來指向管理此裝置的模組。當開始使用某個裝置時,核心使用try_module_get(dev->owner)去增加管理此裝置的owner模組的使用計數;當不再使用此裝置時,核心使用module_put(dev->owner)減少對管理此裝置的owner模組的使用計數。這樣,當裝置在使用時,管理此裝置的模組將不能被解除安裝。只有當裝置不再被使用時,模組才允許被解除安裝。
在Linux 2.6以後的核心下,對於裝置驅動而言,很少需要親自呼叫try_module_get()與module_put(),因為此時開發人員所寫的驅動通常為支援某具體裝置的owner模組,對此裝置owner模組的計數管理由核心裡更底層的程式碼如匯流排驅動或是此類裝置共用的核心模組來實現,從而簡化了裝置驅動開發。

模組的編譯

我們可以為程式碼清單4.1的模板編寫一個簡單的Makefile:

KVERS = $(shell uname -r)

# Kernel modules
obj-m += hello.o

# Specify flags for the module compilation.
#EXTRA_CFLAGS=-g -O0


build: kernel_modules

kernel_modules:
      make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules

clean:
      make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean

該Makefile檔案應該與原始碼hello.c位於同一目錄,開啟其中的EXTRA_CFLAGS=-g -O0可以得到包含除錯資訊的hello.ko模組。執行make命令得到的模組可直接在PC上執行。
如果一個模組包括多個.c檔案(如file1.c、file2.c),則應該以如下方式編寫Makefile:
obj-m := modulename.o
modulename-objs := file1.o file2.o    

模組與GPL

Linux核心有2種方法匯出符號給模組使用,一種方法是EXPORT_SYMBOL(),另外一種EXPORT_SYMBOL_GPL()。這一點和模組A匯出符號給模組B用是一致的。
核心的Documentation/DocBook/kernel-hacking.tmpl明確表明“the symbols exported by EXPORT_SYMBOL_GPL()can only be seen by modules with a MODULE_LICENSE() that specifies a GPL compatible license.”由此可見核心用EXPORT_SYMBOL_GPL()匯出的符號是不可以被非GPL模組引用的。
由於相當多的核心符號都是以EXPORT_SYMBOL_GPL()匯出的,所以歷史上曾經有一些公司的做法是把核心的EXPORT_SYMBOL_GPL()直接改為EXPORT_SYMBOL(),然後將修改後的核心以GPL形式釋出。這樣修改核心之後,模組不再使用核心的EXPORT_SYMBOL_GPL()符號,因此模組不再需要GPL。對此Linus的回覆是:“I think both them said that anybody who were to change a xyz_GPL to the non-GPL one in order to use it with a non-GPL module would almost immediately fall under the "willful infringement" thing, and that it would make it MUCH easier to get triple damages and/or injunctions, since they clearly knew about it”。因此,這種做法可能構成“蓄意侵權(willful infringement)”。
    另外一種做法是寫一個wrapper核心模組(這個模組遵循GPL),把EXPORT_SYMBOL_GPL()匯出的符號封裝一次再次以EXPORT_SYMBOL()形式匯出,而其他的模組不直接呼叫核心而是呼叫wrapper函式,如圖4.1所示。這種做法也具有爭議。
 
圖4.1將EXPORT_SYMBOL_GPL重新以EXPORT_SYMBOL匯出
一般認為,保守的做法是Linux核心不能使用非GPL許可權。


相關推薦

Linux核心模組程式設計核心模組LICENSE -《3

Linux核心模組簡介Linux核心的整體結構已經非常龐大,而其包含的元件也非常多。我們怎樣把需要的部分都包含在核心中呢?一種方法是把所有需要的功能都編譯到Linux核心。這會導致兩個問題,一是生成的核心會很大,二是如果我們要在現有的核心中新增或刪除功能,將不得不重新編譯核心

Linux內核模塊編程內核模塊LICENSE -《具體解釋3

dev sdn hack 認識 方式 flags sin arr 整數 Linux內核模塊簡單介紹Linux內核的總體結構已經很龐大,而其包括的組件或許多。我們如何把須要的部分都包括在內核中呢?一種方法是把全部須要的功能都編譯到Linux內核。這會導致兩個問題。一是生成

Python核心程式設計3PDF

百度雲盤 連結:https://pan.baidu.com/s/1qqsMSl7tIZFnK1EbysIISQ 提取碼:owmm Python核心程式設計(第3版)是經典暢銷圖書《Python核心程式設計(第二版)》的全新升級版本,總共分為3部分。第1部分為講解了Python的一些通用應用,包括正則表示

Python核心編程3PDF

int ofo web編程 相關 iis offic fff 經典 app 百度雲盤鏈接:https://pan.baidu.com/s/1qqsMSl7tIZFnK1EbysIISQ 提取碼:owmm Python核心編程(第3版)是經典暢銷圖書《Python核心編程(第

《神經網絡機器學習3》高清英文PDF+中文PDF

ges 目錄 中文版 href col watermark icu 對比 ofo 下載:https://pan.baidu.com/s/14wAIcUOO3HQEyfz23idldA 《神經網絡與機器學習(第3版)》高清英文PDF+中文PDF經典的《神經網絡與機器學習(第3

《JavaScript高階程式設計3》高清中文版.pdf

書籍簡介: 《JavaScript高階程式設計(第3版)》是JavaScript超級暢銷書的最新版。ECMAScript5和HTML5在標準之爭中雙雙勝出,使大量專有實現和客戶端擴充套件正式進入規範,同時也為JavaScript增添了很多適應未來發展的新特性。 《JavaScript高階程

分享《OpenCV計算機視覺程式設計攻略3》高清中文版PDF+英文版PDF+原始碼

下載:https://pan.baidu.com/s/1QF4MXrlc0JZlO1zSuBLmKQ 《OpenCV計算機視覺程式設計攻略(第3版)》高清中文版PDF+英文版PDF+原始碼 高清中文版PDF,328頁,帶書籤目錄,文字可以複製。高清英文版PDF,464頁,帶書籤目錄,文字可以複製。 中英

《Python程式設計3》[美] 約翰·策勒John Zelle 2 章 答案

判斷對錯1.編寫程式的好方法是立即鍵入一些程式碼,然後除錯它,直到它工作。2.可以在不使用程式語言的情況下編寫演算法。3.程式在寫入和除錯後不再需要修改。4.Python 識別符號必須以字母或下劃線開頭。5.關鍵詞是好的變數名。6.表示式由文字、變數和運算子構成。7.在 Python 中,x = x + 1

《Python程式設計3》[美] 約翰·策勒John Zelle 4 章 答案

判斷對錯 1、利用 grAphiCs.py 可以在 Python 的 shell 視窗中繪製圖形。2、傳統上,圖形視窗的左上角座標為(0,0)。3、圖形螢幕上的單個點稱為畫素。4、建立類的新例項的函式稱為取值方法。5、例項變數用於在物件記憶體儲資料。6、語句 myShApe.move(10,20) 將 my

分享《神經網絡機器學習3》+PDF+Simon Haykin+申富饒

aid 分享圖片 shadow sha tex size 對比 pro 詳細 下載:https://pan.baidu.com/s/1oSr1a2pT-r2DCB2v2XcLHQ 更多資料分享:http://blog.51cto.com/14087171 《神經網絡與機器學

《OpenCV計算機視覺程式設計攻略3》高清中文版PDF+英文版PDF+原始碼

資源連結:https://pan.baidu.com/s/1NHKXsHapQwuuRNLV8i6L-A《OpenCV計算機視覺程式設計攻略(第3版)》高清中文版PDF+英文版PDF+原始碼高清中文版PDF,328頁,帶書籤目錄,文字可以複製。高清英文版PDF,464頁,帶書籤目錄,文字可以複製。中英文兩版可

Python 程式設計3

內容簡介 本書以 Python 語言為工具教授計算機程式設計,強調解決問題、設計和程式設計是電腦科學的核心技能。本書特色鮮明、示例生動有趣、內容易讀易學,適合 Python 入門程式設計師閱讀,也適合高校計算機專業的教師和學生參考。 本書具有以下特點: 廣泛使用計算機圖形學——本書提供一

OpenCV計算機視覺程式設計攻略3完整高清.pdf

【下載地址】 本書結合C++和OpenCV全面講解計算機視覺程式設計,不僅涵蓋計算機視覺和影象處理的基礎知識,而且通過完整示例講解OpenCV的重要類和函式。主要內容包括OpenCV庫的安裝和部署、影象增強、畫素操作、圖形分析等各種技術,並且詳細介紹瞭如何處理來自檔案或攝像機的視訊,以及

《JavaScript高階程式設計3三天 引用型別

/*====================================================== *@author wf_Huang *@Time 2018/9/18 20:06 *=======================================

《OpenCV計算機視覺程式設計攻略3》高清中文版PDF+英文版PDF+原始碼免費下載

網盤下載:https://pan.baidu.com/s/1dGIFxwzsqqXUK8RLgtOzvA 《OpenCV計算機視覺程式設計攻略(第3版)》高清中文版PDF+英文版PDF+原始碼免費下載 高清中文版PDF,328頁,帶書籤目錄,文字可以複製。 高清英文版PDF,464頁,帶書籤目錄,文

6.4Android程式設計權威指南3————六章程式碼報告編譯版本、限制作弊次數

報告編譯版本 關鍵程式碼 xml檔案 <TextView android:id="@+id/tv_compile_version" android:layout_width="wrap_content" android:layout

6.3Android程式設計權威指南3————安全新增新版本API中的程式碼

應用的SDK最低版本和編譯版本間的差異較大,由此帶來的相容性問題需要處理。例如,在應用中,如果呼叫了KitKat(API 19級)以後的SDK版本中的程式碼會怎麼樣呢?結果顯示,在 KitKat 裝置上安裝執行時,應用會崩潰。 假如APP的minSDKVersion是19,新增如下程式碼

6.2Android程式設計權威指南3————Android版本相容、最低版本、目標版本、編譯版本

Android SDK與版本相容 各種裝置遲緩的版本升級再加上Google定期的新版本釋出,給Android程式設計帶來了嚴重的相容性問題。對於增量版本,向下相容一般問題不大。主要版本向下相容才是大麻煩。也就是說,僅支援5.x版本的工作量不大,但需要支援到4.x的話,考慮到這麼多不同版本的

6.1Android程式設計權威指南3————Android SDK版本

每一個有釋出代號的版本隨後都會有相應的增量版本。例如,Ice Cream Sandwich最初的釋出版本為Android 4.0(API 14級),但沒過多久,Android 4.0.3及4.0.4(API 15級)的增量發行版本就取代了它。 為什麼仍有這麼多裝置執行著老版本And

5.1Android程式設計權威指南3————五章程式碼

activity_quiz.xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"