# 一個驅(qū)動程序的角色是提供機制,而不是策略 2010-11-15 09:26 小默
“一個驅(qū)動程序的角色是提供機制,而不是策略。”--ldd3
機制:提供什么能力
策略:如何使用這些能力
機制和策略由軟件不同部分,或完全不同的軟件實現(xiàn)。
比如第一次實習(xí)時:
我們這邊負責(zé)寫驅(qū)動,只關(guān)注實現(xiàn)什么功能,怎么實現(xiàn)這樣功能,這是機制。我們可以直接在設(shè)備管理器中安裝卸載,或者用命令行安裝卸載使用等,隨意,也就是開發(fā)過程完全不考慮策略。
等開發(fā)進行到了一定階段,又招了另外一名同學(xué)負責(zé)界面,這是策略。用戶怎么使用這個驅(qū)動,操作界面是怎樣的,是由他來負責(zé)的。
不知道是不是這個意思O(∩_∩)O~~ 回復(fù) 更多評論 刪除評論 修改評論
# makefile寫法 2010-11-15 17:14 小默
對于單個.c文件的hello world例子:
obj-m := hello.o
從目標(biāo)文件hello.o簡歷一個模塊hello.ko
如果模塊來自兩個源文件file1.c和file2.c:
obj-m := module.o
module-objs := file1.o file2.o
上面的命令,必須在內(nèi)核系統(tǒng)上下建立文中被調(diào)用
-----
在任意當(dāng)前工作目錄中,需要在makefile中指明源碼樹的路徑。
ifneq ($(KERNELRELEASE),)
obj-m := hello.o
else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build //TODO 不是在源碼樹中么/(ㄒoㄒ)/~~
PWD := $(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
endif
創(chuàng)建一個模塊需要調(diào)用兩次上面的makefile
第一次:沒有設(shè)置變量KERNELRELEASE,通過已安裝模塊目錄中的符號鏈接指回內(nèi)核建立樹;然后運行default中的make命令,第二次使用makefile
第二次:已經(jīng)設(shè)置了變量KERNELRELEASE,直接obj-m := hello.o創(chuàng)建模塊 回復(fù) 更多評論 刪除評論 修改評論
# 加載和卸載模塊 2010-11-15 17:57 小默
modprobe實現(xiàn)和insmod一樣加載模塊到內(nèi)核的功能
不同的是,加載前會檢查模塊中是否有當(dāng)前內(nèi)核中沒有定義的symbol,如果有,在模塊搜索路徑中尋找其它模塊是否含有上面的symbol,有的話,自動加載關(guān)聯(lián)模塊
insmod對于這種情況,會報錯unresolved symbols
查看當(dāng)前加載模塊:
lsmod
cat /proc/modules
回復(fù) 更多評論 刪除評論 修改評論
# 如果你的模塊需要輸出符號給其他模塊使用 2010-11-15 18:12 小默
EXPORT_SYMBOL(name);
EXPORT_SYMBOL_GPL(name); 回復(fù) 更多評論 刪除評論 修改評論
# 模塊參數(shù) 2010-11-15 18:40 小默
說10次hello,Mom
# insmod hellop howmany=10 whom="Mom"
--
static char *whom = "world"; //必須給默認值
static int howmany = 1;
module_param(howmany, int, S_IRUGO);
module_param(whom, charp, S_IRUGO);
--
S_IRUGO 允許所有人讀
S_IRUGO | S_IWUSR 允許所有人讀,允許root改變參數(shù)
如果參數(shù)被sysfs修改,不會通知模塊。不要使參數(shù)可寫,除非準(zhǔn)備好檢測參數(shù)改變。
--
數(shù)組參數(shù):
module_param_array(name, type, num, perm); 回復(fù) 更多評論 刪除評論 修改評論
# 設(shè)備主次編號 2010-11-16 13:24 小默
$ ls -l /dev
...
crw-rw---- 1 vcsa tty 7, 132 Nov 15 17:16 vcsa4
crw-rw---- 1 vcsa tty 7, 133 Nov 15 17:16 vcsa5
crw-rw---- 1 vcsa tty 7, 134 Nov 15 17:16 vcsa6
crw-rw---- 1 root root 10, 63 Nov 15 17:16 vga_arbiter
drwxr-xr-x 2 root root 80 Nov 15 17:16 vg_colorfulgreen
crw-rw-rw- 1 root root 1, 5 Nov 15 17:16 zero
...
輸出第一列是c的是字符設(shè)備,第一列b塊設(shè)備
修改日期前的兩個數(shù)字。
第一個是主設(shè)備編號:標(biāo)識設(shè)備相連的驅(qū)動
第二個是次設(shè)備編號:決定引用哪個設(shè)備
-------
設(shè)備編號的內(nèi)部表示
dev_t 在<linux/types.h>中定義,32位,12位主編號,20位次編號。
獲得一個dev_t的主或次編號:<linux/kdev_t.h>
MAJOR(dev_t dev);
MINOR(dev_t dev);
將主次編號轉(zhuǎn)換成dev_t:
MKDEV(int major, int minor);
--------
分配和釋放設(shè)備編號
建立一個字符驅(qū)動時,做的第一件事就是獲取一個或多個設(shè)備編號使用:
<linux/fs.h>
int register_chrdev_region(dev_t first, unsigned int count, char *name);
first要分配的起始設(shè)備編號
count請求的連續(xù)設(shè)備編號的總數(shù)
name連接到這個編號范圍的設(shè)備的名字,會出現(xiàn)在/proc/devices和sysfs中
成功返回0,出錯返回負的錯誤碼。
如果事先不知道使用哪個設(shè)備編號,使用下面函數(shù),內(nèi)核會分配一個主設(shè)備編號:
int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);
dev是一個輸出參數(shù),返回分配范圍的第一個數(shù)
firstminor請求第一個要用的次編號,常是0
設(shè)備編號的釋放:
void unregister_chrdev_region(dev_t first, unsigned int count); 回復(fù) 更多評論 刪除評論 修改評論
# scull安裝腳本 2010-11-16 13:57 小默
腳本scull_load:
1 #!/bin/sh
2 module="scull"
3 device="scull"
4 mode="664"
5
6 # invoke insmod with all arguments we got
7 # and use a pathname, as newer modutils don't look in. by default
8 /sbin/insmod ./$module.ko $* || exit 1 # 插入模塊,使用獲取的所有參數(shù)($*)
9
10 # remove stale nodes刪除無效的節(jié)點,不能刪除device0,device1,device2...阿。。TODO
11 rm -f /dev/${device}[0-3]
12
13 major=$(awk "\\$2==\"$module\" {print \\$1}" /proc/devices) #TODO 沒有搞明白
14 mknod /dev/${device}0 c $major 0 #創(chuàng)建4個虛擬設(shè)備
15 mknod /dev/${device}1 c $major 1
16 mknod /dev/${device}2 c $major 2
17 mknod /dev/${device}3 c $major 3
18
19 # give appropriate group/permissions, and change the group.
20 # Not all distributions have staff, some have "wheel" instead. #TODO 神馬意思?
21 group="staff"
22 grep -q '^staff:' /etc/group || group="wheel"
23 # 改變設(shè)備的組和模式。腳本必須以root運行,但設(shè)備使用可能需要其它用戶寫。
24 chgrp $group /dev/${device}[0-3]
25 chmod $mode /dev/${device}[0-3]
26
回復(fù) 更多評論 刪除評論 修改評論
# file_operations結(jié)構(gòu) 2010-11-16 15:24 小默
一些處理文件的回調(diào)函數(shù)
1 // init file_operations
2 struct file_operations scull_fops = {
3 .owner = THIS_MODULE,
4 .llseek = scull_llseek,
5 .read = scull_read,
6 .write = scull_write,
7 .ioctl = scull_ioctl,
8 .open = scull_open,
9 .release = scull_release,
10 }; 回復(fù) 更多評論 刪除評論 修改評論
# 注冊字符設(shè)備 2010-11-16 15:25 小默
12 // 使用struct scull_dev結(jié)構(gòu)表示每個設(shè)備
13 // TODO 沒有理解什么意思
14 struct scull_dev {
15 struct scull_qset *data; // pointer to first quantum set
16 int quantum; // the current quantum size
17 int qset; // the current array size
18 unsigned long sizee; //amount of data stored here
19 unsigned int access_key; // used by sculluid and scullpriv
20 struct semaphore sem; // matual exclusion semaphore
21 struct cdev cdev; // 字符設(shè)備結(jié)構(gòu)
22 };
23
24 // 初始化struct cdev,并添加到系統(tǒng)中
25 static void scull_setup_cdev(struct scull_dev *dev, int index)
26 {
27 int err, devno = MKDEV(scull_major, scull_minor + index);
28
29 // TODO 初始化已經(jīng)分配的結(jié)構(gòu). 不是很理解
30 // cdev結(jié)構(gòu)嵌套在struct scull_dev中,必須調(diào)用cdev_init()來初始化cdev結(jié)構(gòu)
31 cdev_init(&dev->cdev, &scull_fops);
32 dev->cdev.owner = THIS_MODULE;
33 dev->cdev.ops = &scull_fops;
34 // 添加到系統(tǒng)中
35 err = cdev_add(&dev->cdev, devno, 1);
36 if(err)
37 printk(KERN_NOTICE "Error %d adding scull%d", err, index);
38 }
回復(fù) 更多評論 刪除評論 修改評論
# system-config-selinux 2010-11-27 02:54 小默
system-config-selinux
什么時候改了,汗 //TODO 回復(fù) 更多評論 刪除評論 修改評論
# read & write 2010-11-30 22:12 小默
// 表示每個設(shè)備
struct scull_dev{
struct scull_qset *data; // pointer to first quantum set
int quantum; // the current quantum size - 當(dāng)前量子和量子集大小
int qset; // the current array size - 每個內(nèi)存區(qū)域為一個量子,數(shù)組為一個量子集
unsigned long size; // amount of data stored here
unsigned int access_key; // used by sculluid and scullpriv
struct semaphore sem; // mutual exclusion semaphore
struct cdev cdev; // char device structure
};
// 量子集,即一個內(nèi)存區(qū)域的數(shù)組
struct scull_qset{
void **data;
struct scull_qset *next;
};
ssize_t scull_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
struct scull_dev *dev = file->private_data;
struct scull_qset *dptr; // 量子集中的第一個元素
int quantum = dev->quantum, qset = dev->qset; // 當(dāng)前量子和量子集大小
int itemsize = quantum * qset; // listitem中的字節(jié)數(shù)=量子大小*量子集大小
int item, s_pos, q_pos, rset;
ssize_t retval = 0;
if(down_interruptible(&dev->sem)) // TODO
return -ERESTARTSYS;
if(*f_pos > dev->size)
goto out;
if(*f_pos + count > dev->size)
count = dev->size - *f_pos;
// 查找listitem, qset index, and 量子中的偏移量
item = (long)*f_pos / itemsize;
rest = (long)*f_pos % itemsize;
s_pos = rest / quantum;
q_pos = rest % quantum;
// 遍歷list到右側(cè)
dptr = scull_follow(dev, item); // 量子集中的第一個元素
if(dptr == NULL || !dptr->data || !dptr->data[s_pos])
goto out;
// 只讀取到這個量子的尾部
if(count > quantum - q_pos)
count = quantum - q_pos;
if(copy_to_user(buf, dptr->data[s_pos] + q_pos, count)){
retval = -EFAULT;
goto out;
}
*f_pos += count;
retval = count;
out:
up(&dev->sem);
return retval;
}
// 一次處理單個量子
ssize_t scull_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
{
struct scull_dev *dev = filp->private_data;
struct scull_qset *dptr;
int quantum = dev->quantum, qset = dev->qset;
int itemsize = quantum * qset;
int item, s_pos, q_pos, rest;
ssize_t retval = -ENOMEM; // value used in "goto out" statements
if(down_interruptible(&dev->sem))
return -ERESTARTSYS;
// 查找列表元素,qset index and 量子中的偏移量
item = (long)*f_pos / itemsize;
rest = (long)*f_pos % itemsize;
s_pos = rest / quantum;
q_pos = rest % quantum;
// 遍歷list到右側(cè)
dptr = scull_follow(dev, item);
if(dptr == NULL):
goto out;
if(!dptr->data){
dptr->data = kmalloc(qset * sizeof(char), GPL_KERNEL);
if(!dptr->data)
goto out;
memset(dptr->data, 0, qset * sizeof(char *));
}
if(!dptr->data[s_pos]){
dptr->data[s_pos] = kmalloc(quantum, GFP_KERNEL);
if(!dptr->data[s_pos])
goto out;
}
// 只寫到這個量子的結(jié)束
if(count > quantum-q_pos)
count = quantum - q_pos;
// 從用戶空間拷貝一整段數(shù)據(jù)to from count
if(copy_from_user(dptr->data[s_pos]+q_pos, buf, count)){
retval = -EFAULT;
goto out;
}
*f_pos += count;
retval = count;
// 更新size
if(dev->size < *f_pos)
dev->size = *f_pos;
out:
up(&dev->sem);
return retval;
}
//-------------------------------
// read和write的"矢量"版本
// readv輪流讀取指示的數(shù)量到每個緩存;writev收集每個緩存的內(nèi)容到一起并且作為單個寫操作送出它們。
// count參數(shù)告訴有多少iovec結(jié)構(gòu),這些結(jié)構(gòu)由應(yīng)用程序創(chuàng)建,但是內(nèi)核在調(diào)用驅(qū)動之前拷貝它們到內(nèi)核空間。
ssize_t (*readv)(struct file *filp, const struct iovec *iov, unsigned long count, loff_t *ppos);
ssize_t (*writev)(struct file *filp, const struct iovec *iov, unsigned long count, loff_t *ppos);
// iovec描述了一塊要傳送的數(shù)據(jù)
struct iovec{
void __user *iov_base; // 開始于iov_base(在用戶空間)
__kernel_size_t iov_len; // 并有iov_len長
};
回復(fù) 更多評論 刪除評論 修改評論
# 重定向控制臺消息 2010-11-30 22:44 小默
// 重定向控制臺消息
// 使用一個參數(shù)指定接收消息的控制臺的編號
int main(int argc, char **argv)
{
char bytes[2] = {11, 0}; // 11 是 TIOCLINUX 的功能號
if(argc == 2) bytes[1] = atoi(argv[1]); // the chosen console
else{
fprintf(stderr, "%s: need a single arg\n", argv[0]);
exit(1);
}
// TIOCLINUX傳遞一個指向字節(jié)數(shù)組的指針作為參數(shù),數(shù)組的第一個字節(jié)是一個數(shù)(需要指定的子命令)。
// 當(dāng)子命令是11時,下一個字節(jié)指定虛擬控制臺。
if(ioctl(STDIN_FILENO, TIOCLINUX, bytes)<0){ // use stdin
fprintf(stderr, "%s: ioctl(stdin, TIOCLINUX): %s\n", argv[0], stderror(errno));
exit(1);
}
exit(0);
}
回復(fù) 更多評論 刪除評論 修改評論
# Implementing files in /proc 2010-12-04 00:42 小默
// 在proc里實現(xiàn)文件,在文件被讀時產(chǎn)生數(shù)據(jù)。
// 當(dāng)一個進程讀你的/proc文件,內(nèi)核分配了一頁內(nèi)存,驅(qū)動可以寫入數(shù)據(jù)返回給用戶空間。
// buf 寫數(shù)據(jù)的緩沖區(qū);start有關(guān)數(shù)據(jù)寫在頁中哪里;eof必須被驅(qū)動設(shè)置,表示寫數(shù)據(jù)結(jié)束;data用來傳遞私有數(shù)據(jù)。
// 假定不會有必要產(chǎn)生超過一頁的數(shù)據(jù),并且因此忽略了start和offset值。
int scull_read_procmem(char *buf, char **start, off_t offset, int count, int *eof, void *data)
{
int i, j, len = 0;
int limit = count - 80; // Don't print more than this
for(i = 0; i < scull_nr_devs && len <= limit; i++){ // TODO scull_nr_devs ?
struct scull_dev *d = &scull_devices[i];
struct scull_qset *qs = d->data;
if(down_interruptible(&d->sem))
return -ERESTARTSYS;
// 設(shè)備號,量子集大小,量子大小,存儲的數(shù)據(jù)量
len += sprintf(buf+len, "\nDevice %i: qset %i, q %i, sz %li\n", i, d->qset, d->quantum, d->size);
for(; qs && len <= limit; qs = qs->next){ //scan the list 遍歷量子鏈表
// 元素地址、鏈表地址
len += sprintf(buf + len, " item at %p, qset at %p\n", qs, qs->data); // %p 顯示一個指針
if(qs->data && !qs->next) // dump only the last item
for(j = 0; j < d->qset, j++){
if(qs->data[j])
len += sprintf(buf+len, "%4i: %8p\n", j, qs->data[j]);
}
}
up(&scull_devices[i]);
}
*eof = 1;
return len; // 返回實際在頁中寫了多少數(shù)據(jù)
}
/// 移除entry的一些問題
// 移除可能發(fā)生在文件正在被使用時。/proc入口沒有owner,沒有引用計數(shù)。
// 內(nèi)核不檢查注冊的名字是否已經(jīng)存在,可能會有多個entry使用相同名稱。而且在訪問和remove_proc_entry時,它們沒有區(qū)別。。。悲劇。。。 回復(fù) 更多評論 刪除評論 修改評論
# The seq_file interface 2010-12-04 10:56 小默
// 創(chuàng)建一個虛擬文件,遍歷一串?dāng)?shù)據(jù),這些數(shù)據(jù)必須返回用戶空間。
// start, next, stop, show
// sfile 總被忽略;pos指從哪兒開始讀,具體意義完全依賴于實現(xiàn)。
// seq_file典型的實現(xiàn)是遍歷一感興趣的數(shù)據(jù)序列,pos就用來指示序列中的下一個元素。
// 在scull中,pos簡單地作為scull_array數(shù)組的索引。
// 原型
void *start(struct seq_file *sfile, loff_t *pos);
// 在scull中的實現(xiàn)
static void *scull_seq_start(struct seq_file *s, loff_t *pos)
{
if(*pos >= scull_nr_devs)
return NULL; // no more to read
return scull_devices + *pos; // 返回供迭代器使用的私有數(shù)據(jù)
}
// next把迭代器后挪一位,返回NULL表示沒有更多數(shù)據(jù)了
// v:上一次start/next調(diào)用返回的迭代器 TODO ???返回的不是私有數(shù)據(jù)么?
// pos: 文件中的當(dāng)前位置。
void *next(struct seq_file *sfile, void *v, loff_t *pos);
// scull的實現(xiàn)
static void *scull_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
(*pos)++;
if(*pos >= scull_nr_devs)
return NULL;
return scull_devices + *pos;
}
// 內(nèi)核完成迭代器,調(diào)用stop清理
void stop(struct seq_file *sfile, void *v);
// scull沒有要清理的東西,stop方法是空的
// start到stop期間不會有sleep或者非原子操作,可以放心的在start中獲得信號量或自旋鎖。整個調(diào)用序列都是原子的。天書啊 TODO ???
// 在start和stop期間,內(nèi)核調(diào)用show輸出迭代器v生成的數(shù)據(jù)到用戶空間
int show(struct seq_file *sfile, void *v);
// 輸出,等效于用戶空間的printf。返回非0值表示緩沖滿,輸出的數(shù)據(jù)會被丟棄。不過大多數(shù)實現(xiàn)都忽略返回值。
int seq_sprintf(struct seq_file *sfile, const char *fmt, ...);
// 等效于用戶空間的putc和puts
int seq_putc(struct seq_file *sfile, char c);
int seq_puts(struct seq_file *sfile, const char *s);
// 如果s中有esc中的數(shù)據(jù),這些數(shù)據(jù)用8進制輸出。常見的esc是"\t\n\\",用于保持空格,避免搞亂輸出。
int seq_escape(struct seq_file *m, const char *s, const char *esc);
// scull中show實現(xiàn)
static int scull_seq_show(struct seq_file *s, void *v)
{
struct scull_dev *dev = (struct scull_dev *)v;
struct scull_qset *d;
int i;
if(down_interrutible(&dev->sem))
return -ERESTARTSYS;
seq_printf(s, "\nDevice %i: qset %i, q %i, sz %li\n",
(int)(dev - scull_devices), dev->qset,
dev->quantum, dev->size);
for(d = dev->data; d; d = d->next){ // 遍歷鏈表
seq_printf(s, " item at %p, qset at %p\n", d, d->data);
if(d->data && !d->next) // dump only the last item
for(i = 0; i < dev->qset; i++){
if(d->data[i])
seq_printf(s, " %4i: %8p\n", i, d->data[i]);
}
}
up(&dev->sem);
return 0;
}
// 迭代器:指向scull_dev的一個指針,囧。。。
// 迭代器操作集
static struct seq_operations scull_seq_ops = {
.start = scull_seq_start,
.next = scull_seq_next,
.stop = scull_seq_stop,
.show = scull_seq_show
};
/// 用file_operations結(jié)構(gòu)結(jié)構(gòu),實現(xiàn)內(nèi)核read/seek文件的所有操作。
// 創(chuàng)建一個open方法,連接文件和seq_file操作 TODO 沒看懂
static int scull_proc_open(struct inode *inode, struct file *file)
{
// seq_open 連接文件和上面定義的scull_seq_ops
return seq_open(file, &scull_seq_ops);
}
// file_operations結(jié)構(gòu)
static struct fle_operations scull_proc_ops = {
.owner = THIS_MODULE,
.open = scull_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release
};
// 在/proc中創(chuàng)建設(shè)備
entry = create_proc_entry("scullseq", 0, NULL);
if(entry)
entry->proc_fops = &scull_proc_ops;
// create_proc_entry原型
struct proc_dir_entry *create_proc_entry(const char *name,
mode_t, mode,
struct proc_dir_entry *parent); 回復(fù) 更多評論 刪除評論 修改評論
# strace命令 - debug 2010-12-04 14:12 小默
略 O(∩_∩)O~~ 回復(fù) 更多評論 刪除評論 修改評論
# ldd3_4.5_Debugging System Faults 2010-12-05 14:46 小默
講了兩部分
一、system opps
空指針引用,或者局部變量賦值覆蓋了原eip,導(dǎo)致頁錯誤
二、系統(tǒng)掛起 // TODO 看得云里霧里的
死循環(huán)等引起
假掛起:鼠標(biāo)鍵盤等外設(shè)沒有響應(yīng)了,系統(tǒng)實際正常。可以看時間。。。
插入schedule調(diào)用防止死循環(huán),作用是允許其他進程從當(dāng)前進程竊取時間。講了些弊端,沒看懂
sysrq:沒看懂
回復(fù) 更多評論 刪除評論 修改評論
# re: 蓋樓 2010-12-26 06:13 小默
所謂原子操作,就是該操作絕不會在執(zhí)行完畢前被任何其他任務(wù)或事件打斷,也就說,它的最小的執(zhí)行單位,不可能有比它更小的執(zhí)行單位。
原子操作主要用于實現(xiàn)資源計數(shù),很多引用計數(shù)(refcnt)就是通過原子操作實現(xiàn)的。
原子類型定義:
typedef struct
{
volatile int counter;
}
atomic_t;
volatile修飾字段告訴gcc不要對該類型的數(shù)據(jù)做優(yōu)化處理,對它的訪問都是對內(nèi)存的訪問,而不是對寄存器的訪問。 回復(fù) 更多評論刪除評論 修改評論
# spinlock_t 2010-12-26 16:24 小默
17 typedef struct {
18 volatile unsigned int lock;
19 #ifdef CONFIG_DEBUG_SPINLOCK
20 unsigned magic;
21 #endif
22 } spinlock_t; 回復(fù) 更多評論 <a id="AjaxHolder_Comments_CommentList_ctl24_DeleteLink"