方法列表:html
1.系統調用node
2.虛擬文件系統
proc文件系統
sysfs文件系統
debugfs文件系統linux
3.ioctl接口git
4.netlink
調試方法:
https://blog.csdn.net/gatieme/article/details/68948080shell
一:系統調用
1.簡介編程
https://blog.csdn.net/gatieme/article/details/50779184
https://blog.csdn.net/liduxun/article/details/48119849
https://www.cnblogs.com/zl1991/p/6543634.htmlapi
1.爲用戶空間提供了一種硬件的抽象接口。
2.系統調用保證了系統的穩定性和安全。
3.系統調用是用戶空間訪問內核的惟一手段:除異常和陷入外,它們是內核惟一的合法入口。實際上,其它的像設備文件和/proc之類的方式,最終也仍是要經過系統調用進行訪問的。緩存
1.通常狀況下,應用程序經過在用戶空間實現的應用編程接口(API)而不是直接經過系統調用來編程。(好處:更大的兼容性,而無論系統調用的實現。)安全
2.在unix世界中,最流行的的應用編程接口是基於POSIX標準的。POSIX是由IEEE的標準組成,其目的是提供一套大致上基於Unix的可移植操做系統標準。POSIX是說明API和系統調用之間關係的一個極好例子。
3.C庫實現了Unix系統的主要API,包括標準C庫函數和系統調用接口。C庫提供了POSIX的絕大部分API。
4.Unix的系統調用抽象出了用於完成某種肯定的目的的函數。「提供機制而不是策略」數據結構
在內核中的實現:
SYSCALL_DEFINE0(getpid) { return task_tgid_vnr(current); //return current->tgid }
asmlinkage long sys_getpid(void)
1.注意函數聲明中 asmlinkage限定詞,這是一個編譯指令,通知編譯器僅從棧中提取該函數的參數。全部的系統調用都須要這個限定詞。
2.函數返回long。爲保證32位和64位系統的兼容,系統調用在用戶空間和內核空間有不一樣的返回類型,在用戶空間爲int,在內核空間爲long。
3.注意系統調用get_pid()在內核中被定義乘sys_getpid()。這是linux中全部系統調用都應該遵照的命名規則,系統調用bar()在內核中也實現爲sys_bar()函數。
當系統調用返回時,控制權仍然在system_call()中,它最終會負責切換到用戶空間,並讓用戶進程繼續執行下去。
2.應用接口
1.編寫一個系統調用;
2.在系統調用表的最後加入一個表項;
3 對於所支持的各類體系架構,系統調用號都必須定義域 asm/unistd.h中
4.系統調用必須被編譯進內核映象(不能被編譯成模塊)。這隻要把它放進kernel/下的一個相關文件中就能夠了,好比sys.c,它包含了各類各樣的系統調用。
1.編寫一個系統調用
2.系統調用表內增長條目。 /arch/arm/kernel/calls.S內添加
3.應用實例
/*1.編寫foo()系統調用。咱們把它放入kernel/sys.c文件中。*/ #include <asm/page.h> /* *sys_foo:每一個人喜歡的系統調用 *返回每一個進程的內核棧大小 */ asmlinkage long sys_foo(void) { return THREAD_SIZE; } /*2.加入系統調用表 entry.s*/ ENTRY(sys_call_table) .long sys_restart_syscall /* 0 */ .long sys_exit .long sys_fork .long sys_read .long sys_write .long sys_open /* 5 */ ... .long sys_eventfd2 .long sys_epoll_create1 /* 330 */ .long sys_dup3 .long sys_pipe2 .long sys_inotify_init1 .long sys_preadv .long sys_pwritev /* 335 */ .long sys_rt_tgsigqueueinfo .long sys_perf_event_open .long sys_recvmmsg .long sys_setns .long sys_foo /338 myself syscall/
/* 3.定義系統調用號 asm/unistd.h */
#define __NR_foo 338
arm架構: 內核層
/*1.添加系統調用函數 在/kernel/sys.c內添加*/ asmlinkage long sys_foo(void) { return THREAD_SIZE; } /*2.更新unistd.h 目錄:arch/arm/include/asm/unistd.h (不是這個) arch/arm/include/uapi/asm/unistd.h 注:只能添加在全部系統調用號的最後面*/ #define __NR_foo (__NR_SYSCALL_BASE+387) /*3.系統調用表內增長條目 在/arch/arm/kernel/calls.S內 注:只能添加在全部CALL的最後面,而且與(2)的調用號相對應。不然必定使系統調用表混亂。*/ CALL(sys_foo)
用戶層測試:
#include <unistd.h> #include <sys/syscall.h> #include<stdio.h> int main() { int mem_size = 0; mem_size = syscall(__NR_foo); printf("the process mem size:%d bytes", mem_size); return 0; }
二:虛擬文件系統
2.1 proc文件系統
1.簡介
https://www.ibm.com/developerworks/cn/linux/l-proc.html
https://blog.csdn.net/sty23122555/article/details/51638697
seq操做:
http://www.cnblogs.com/Wandererzj/archive/2012/04/16/2452209.html#commentform
2.應用接口
Linux 3.9以及以前的內核版本:
/*1.建立proc文件*/ //建立目錄 struct proc_dir_entry *proc_mkdir(const char *name, struct proc_dir_entry *parent); /*做用:用於建立"/proc"節點 參數: name:爲"/proc"節點的名稱 parent/base:爲父目錄的節點,若是爲NULL,則指"/proc"目錄。 返回值: create_proc_entry 的返回值是一個 proc_dir_entry 指針(或者爲 NULL,說明在 create 時發生了錯誤)。 而後就可使用這個返回的指針來配置這個虛擬文件的其餘參數,例如在對該文件執行讀操做時應該調用的函數。 注:當read()系統調用在"/proc"文件系統中執行時,它映象到一個數據產生函數,而不是一個數據獲取函數。 */ struct proc_dir_entry *create_proc_entry( const char *name, mode_t mode, struct proc_dir_entry *parent ); struct proc_dir_entry { ...... const struct file_operations *proc_fops; <==文件操做結構體 struct proc_dir_entry *next, *parent, *subdir; void *data; read_proc_t *read_proc; <==讀回調 write_proc_t *write_proc; <==寫回調 ...... }; /*2. 刪除proc文件/目錄*/ /*要從 /proc 中刪除一個文件,可使用 remove_proc_entry 函數。要使用這個函數,咱們須要提供文件名字符串, 以及這個文件在 /proc 文件系統中的位置(parent)。*/ void remove_dir_entry(const char *name, struct proc_dir_entry *parent); /*3. /proc節點的讀寫函數*/ /* proc文件其實是一個叫作proc_dir_entry的struct(定義在proc_fs.h),該struct中有int read_proc和int write_proc 兩個元素,要實現proc的文件的讀寫就要給這兩個元素賦值。但這裏不是簡單地將一個整數賦值過去就好了,須要實現兩個回調函數。 在用戶或應用程序訪問該proc文件時,就會調用這個函數,實現這個函數時只需將想要讓用戶看到的內容放入page便可。 在用戶或應用程序試圖寫入該proc文件時,就會調用這個函數,實現這個函數時須要接收用戶寫入的數據(buff參數)。 */ static int (*proc_read)(char *page, char **start, off_t off, int count, int *eof, void *data); static int proc_write_foobar(struct file *file, const char *buffer, unsigned long count, void *data);
Linux 3.10及之後的內核版本:
「/proc」的內核API和實現架構變動較大,create_proc_entry()、create_proc_read_entry()之類的API都被刪除了,取而代之的是直接使用proc_create()、proc_create_data() API。同時,也再也不存在read_proc()、write_proc()之類的針對proc_dir_entry的成員函數了,而是直接把file_operations結構體的指針傳入proc_create()或者proc_create_data()函數中。
static inline struct proc_dir_entry *proc_create( const char *name, umode_t mode, struct proc_dir_entry *parent, const struct file_operations *proc_fops) { return proc_create_data(name, mode, parent, proc_fops, NULL); } struct proc_dir_entry *proc_create_data(const char *name, umode_t mode, struct proc_dir_entry *parent, const struct file_operations *proc_fops, void *data)
3.實例
Linux 3.9以及以前的內核版本:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/proc_fs.h> static unsigned int variable; static struct proc_dir_entry *test_dir, *test_entry; static int test_proc_read(char *buf, char **start, off_t off, int count,int *eof, void *data) { unsigned int *ptr_var = data; return sprintf(buf, "%u\n", *ptr_var); } static int test_proc_write(struct file *file, const char *buffer,unsigned long count, void *data) { unsigned int *ptr_var = data; *ptr_var = simple_strtoul(buffer, NULL, 10); return count; } static __init int test_proc_init(void) { test_dir = proc_mkdir("test_dir", NULL); if (test_dir) { test_entry = create_proc_entry("test_rw", 0666, test_dir); if (test_entry) { test_entry->nlink = 1; test_entry->data = &variable; test_entry->read_proc = test_proc_read; test_entry->write_proc = test_proc_write; return 0; } } return -ENOMEM; } module_init(test_proc_init); static __exit void test_proc_cleanup(void) { remove_proc_entry("test_rw", test_dir); remove_proc_entry("test_dir", NULL); } module_exit(test_proc_cleanup); MODULE_AUTHOR("Barry Song <baohua@kernel.org>"); MODULE_DESCRIPTION("proc example"); MODULE_LICENSE("GPL v2");
Linux 3.10及之後的內核版本:
#include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/version.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> static unsigned int variable; static struct proc_dir_entry *test_dir, *test_entry; #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) //other code #else static int test_proc_show(struct seq_file *seq, void *v) { unsigned int *ptr_var = seq->private; seq_printf(seq, "%u\n", *ptr_var); return 0; } static ssize_t test_proc_write(struct file *file, const char __user *buffer,size_t count, loff_t *ppos) { struct seq_file *seq = file->private_data; unsigned int *ptr_var = seq->private; *ptr_var = simple_strtoul(buffer, NULL, 10); return count; } static int test_proc_open(struct inode *inode, struct file *file) { return single_open(file, test_proc_show, PDE_DATA(inode)); } static const struct file_operations test_proc_fops = { .owner = THIS_MODULE, .open = test_proc_open, .read = seq_read, .write = test_proc_write, .llseek = seq_lseek, .release = single_release, }; #endif static __init int test_proc_init(void) { test_dir = proc_mkdir("test_dir", NULL); if (test_dir) { #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) //other code #else test_entry = proc_create_data("test_rw", 0666, test_dir, &test_proc_fops, &variable); if (test_entry) { return 0; } #endif } return -ENOMEM; } module_init(test_proc_init); static __exit void test_proc_cleanup(void) { remove_proc_entry("test_rw", test_dir); remove_proc_entry("test_dir", NULL); } module_exit(test_proc_cleanup); MODULE_AUTHOR("Barry Song <baohua@kernel.org>"); MODULE_DESCRIPTION("proc example"); MODULE_LICENSE("GPL v2");
Makefile:
ifneq ($(KERNELRELEASE),) obj-m := proc_test.o else PWD := $(shell pwd) KDIR := /lib/modules/$(shell uname -r)/build all: $(MAKE) -C $(KDIR) M=$(PWD) modules clean: rm -rf .*.cmd *.o *.mod.c *.ko .tmp_versions modules.order Module.symvers endif
2.2:sysfs文件系統
1.簡介
資料:
http://www.wowotech.net/linux_kenrel/dm_sysfs.html
block:包含全部的塊設備。
devices:包含系統全部的設備,並根據設備掛接的總線類型組織成層次結構。
bus:包含系統中全部的總線類型。
class:包含系統中的設備類型(如網卡設備、聲卡設備、輸入設備等)。
2.應用接口
/* _name:名稱,也就是將在sys fs中生成的文件名稱。 _mode:上述文件的訪問權限,與普通文件相同,UGO的格式。 _show:顯示函數,cat該文件時,此函數被調用。 _store:寫函數,echo內容到該文件時,此函數被調用。 */ /*1.driver: sysfs interface for exporting driver attributes */ struct driver_attribute { struct attribute attr; ssize_t (*show)(struct device_driver *driver, char *buf); ssize_t (*store)(struct device_driver *driver, const char *buf, size_t count); }; #define DRIVER_ATTR(_name, _mode, _show, _store) \ struct driver_attribute driver_attr_##_name = __ATTR(_name, _mode, _show, _store) #define DRIVER_ATTR_RW(_name) \ struct driver_attribute driver_attr_##_name = __ATTR_RW(_name) #define DRIVER_ATTR_RO(_name) \ struct driver_attribute driver_attr_##_name = __ATTR_RO(_name) #define DRIVER_ATTR_WO(_name) \ struct driver_attribute driver_attr_##_name = __ATTR_WO(_name) /*2.devices: interface for exporting device attributes */ struct device_attribute { struct attribute attr; ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf); ssize_t (*store)(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); }; ssize_t device_show_ulong(struct device *dev, struct device_attribute *attr, char *buf); ssize_t device_store_ulong(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); ssize_t device_show_int(struct device *dev, struct device_attribute *attr, char *buf); ssize_t device_store_int(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); ssize_t device_show_bool(struct device *dev, struct device_attribute *attr, char *buf); ssize_t device_store_bool(struct device *dev, struct device_attribute *attr, const char *buf, size_t count); #define DEVICE_ATTR(_name, _mode, _show, _store) \ struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store) #define DEVICE_ATTR_RW(_name) \ struct device_attribute dev_attr_##_name = __ATTR_RW(_name) #define DEVICE_ATTR_RO(_name) \ struct device_attribute dev_attr_##_name = __ATTR_RO(_name) #define DEVICE_ATTR_WO(_name) \ struct device_attribute dev_attr_##_name = __ATTR_WO(_name) #define DEVICE_ULONG_ATTR(_name, _mode, _var) \ struct dev_ext_attribute dev_attr_##_name = \ { __ATTR(_name, _mode, device_show_ulong, device_store_ulong), &(_var) } #define DEVICE_INT_ATTR(_name, _mode, _var) \ struct dev_ext_attribute dev_attr_##_name = \ { __ATTR(_name, _mode, device_show_int, device_store_int), &(_var) } #define DEVICE_BOOL_ATTR(_name, _mode, _var) \ struct dev_ext_attribute dev_attr_##_name = \ { __ATTR(_name, _mode, device_show_bool, device_store_bool), &(_var) } #define DEVICE_ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) \ struct device_attribute dev_attr_##_name = \ __ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store) /*3.bus*/ struct bus_attribute { struct attribute attr; ssize_t (*show)(struct bus_type *bus, char *buf); ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count); }; #define BUS_ATTR(_name, _mode, _show, _store) \ struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store) #define BUS_ATTR_RW(_name) \ struct bus_attribute bus_attr_##_name = __ATTR_RW(_name) #define BUS_ATTR_RO(_name) \ struct bus_attribute bus_attr_##_name = __ATTR_RO(_name)
3.實例
/*1.定義接口函數*/ static ssize_t bma253_state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct bma253_data *pdata = &bma253_dev; int enable = 0; enable = atomic_read(&pdata->work_mode); return sprintf(buf, "%d\n", enable); } static ssize_t bma253_state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct bma253_data *pdata = &bma253_dev; int ret; unsigned long enable; if(kstrtoul(buf, 10, &enable) < 0) return -EINVAL; ret = bma253_change_mode(pdata, enable); if (!ret) { atomic_set(&pdata->work_mode, enable); printk(KERN_INFO "power status =0x%x\n", (int)enable); } return count; } /*2.快捷建立*/ static DEVICE_ATTR(enable, 0666, bma253_state_show, bma253_state_store); /*3.*/ static struct attribute *bma253_attributes[] = { &dev_attr_enable.attr, NULL }; /*4.*/ static const struct attribute_group bma253_attr_group = { .attrs = bma253_attributes, }; /*5.在probe中調用,建立sys調試文件*/ result = sysfs_create_group(&bma253_device.this_device->kobj, &bma253_attr_group); if (result) { printk(KERN_ERR "create device file failed!\n"); result = -EINVAL; goto err_create_sysfs; }
完整實例:
#include <linux/init.h> #include <linux/module.h> #include <linux/device.h> #include <linux/ioport.h> #include <linux/errno.h> #include <linux/workqueue.h> #include <linux/platform_device.h> #include <linux/types.h> #include <linux/io.h> typedef struct { int num; } hello_priv; /*1.定義接口函數*/ static ssize_t hello_power_store(struct device *dev,struct device_attribute *attr,const char *buf, size_t count) { hello_priv* prv = dev_get_drvdata(dev); unsigned long power; if(kstrtoul(buf, 10, &power) < 0) return -EINVAL; prv->num = (int)power; return count; } static ssize_t hello_power_show(struct device *dev, struct device_attribute *attr, char *buf) { hello_priv* prv = dev_get_drvdata(dev); int power = 0; power = prv->num; return sprintf(buf, "%d\n", power); } /*2. 設置DEVICE_ATTR: 名稱power_on必須與attribute中的名稱對應*/ static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, hello_power_show, hello_power_store); //show接口爲空,只有store接口,即只支持寫不技持讀 /*3. 設置DEVICE_ATTR*/ static struct attribute *hello_attributes[] = { &dev_attr_power_state.attr, //dev_attr_****.attr中間的××××必須與DEVICE_ATTR中的名稱對應 NULL }; /*4. 封裝到attribute_group中*/ static const struct attribute_group hello_attr_group = { .attrs = hello_attributes, }; static int __init hello_probe(struct platform_device *pdev) { int rc; hello_priv* prv; prv = devm_kzalloc(&pdev->dev, sizeof(hello_priv), GFP_KERNEL); if (!prv) { dev_err(&pdev->dev, "failed to allocate hello\n"); return -ENOMEM; } prv->num = 2; platform_set_drvdata(pdev, prv); /*5.調用sysfs_create_group建立/sys下的文件*/ rc = sysfs_create_group(&pdev->dev.kobj, &hello_attr_group); if (rc) { dev_err(&pdev->dev,"failed to create hello sysfs group\n"); goto fail; } return 0; fail: return rc; } struct platform_driver hello_driver = { .driver = { .name = "hello", .owner = THIS_MODULE, }, .probe = hello_probe,
}; static struct platform_device hello_device = { .name = "hello", .id = -1, }; static int __init hello_init(void) { int ret; ret = platform_device_register(&hello_device); if (ret != 0) printk(KERN_NOTICE "cong: %s:%s[%d]: error", __FILE__,__FUNCTION__, __LINE__); ret = platform_driver_register(&hello_driver); return ret; } static void __init hello_exit(void) { platform_driver_unregister(&hello_driver); } module_init(hello_init); module_exit(hello_exit); MODULE_DESCRIPTION("Hellow test Driver"); MODULE_AUTHOR("vec"); MODULE_LICENSE("GPL");
2.3 debugfs文件繫系統
1.簡介
https://blog.csdn.net/luckywang1103/article/details/26809843
2.應用接口
3.實例
三:ioctl接口
1.簡介
優秀博客:
http://blog.chinaunix.net/uid-25014876-id-59419.html
https://blog.csdn.net/zifehng/article/details/59576539
2.應用接口
用戶空間:
#include <sys/ioctl.h> /* 參數: fd:文件描述符。 cmd:交互協議,設備驅動將根據cmd執行對應操做。 ...:可變參數arg,依賴cmd指定長度以及類型。 返回值: ioctl()執行成功時返回0,失敗則返回-1並設置全局變量errorno值 注: 在實際應用中,ioctl出錯時的errorno大部分是ENOTTY(error not a typewriter),顧名思義, 即第一個參數fd指向的不是一個字符設備,不支持ioctl操做,這時候應該檢查前面的open函數是否 出錯或者設備路徑是否正確。 */ int ioctl(int fd, int cmd, ...) ;
內核空間:
注:
在字符設備驅動開發中,通常狀況下只要實現unlocked_ioctl()便可,由於在vfs層的代碼是直接調用unlocked_ioctl()。
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
ioctl命令,用戶與驅動之間的協議
type設備類型字段:爲一個「幻數」,能夠是0~0xff的值,內核中的ioctl-number.txt給出了一些推薦的和已經被使用的「幻數」,新設備驅動定義「幻數」的時候要避免與其衝突。
nr序列號字段:8位
dir方向字段:表示數據傳送方向,可能的值是 _IOC_NONE(無數據傳輸)、_IOC_READ(讀)、_IOC_WRITE(寫)、_IOC_READ|_IOC_WRITE(雙向)。
size數據尺寸字段:表示涉及的用戶數據的大小,這個成員的寬度依賴於體系結構,一般是13或者14位。
宏-輔助生成命令
做用:根據傳入的type(設備類型字段)、nr(序列號字段)、size(數據長度字段)和宏名隱含的方向字段移位組合生成命令碼。
#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0) #define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size)) #define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size)) #define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size)) /*_IO、_IOR等使用的_IOC宏*/ #define _IOC(dir,type,nr,size) \ (((dir) << _IOC_DIRSHIFT) | \ ((type) << _IOC_TYPESHIFT) | \ ((nr) << _IOC_NRSHIFT) | \ ((size) << _IOC_SIZESHIFT)) eg: #define GLOBALMEM_MAGIC 'g' #define MEM_CLEAR _IO(GLOBALMEM_MAGIC, 0)
3.實例
公用頭文件
ioctl-test.h,用戶空間和內核空間共用的頭文件,包含ioctl命令及相關宏定義,能夠理解爲一份「協議」文件。
// ioctl-test.h #ifndef __IOCTL_TEST_H__ #define __IOCTL_TEST_H__ #include <linux/ioctl.h> // 內核空間 // #include <sys/ioctl.h> // 用戶空間 /* 定義設備類型 */ #define IOC_MAGIC 'c' /* 初始化設備 */ #define IOCINIT _IO(IOC_MAGIC, 0) /* 讀寄存器 */ #define IOCGREG _IOW(IOC_MAGIC, 1, int) /* 寫寄存器 */ #define IOCWREG _IOR(IOC_MAGIC, 2, int) #define IOC_MAXNR 3 struct msg { int addr; unsigned int data; }; #endif
內核空間:
// ioctl-test-driver.c ...... static const struct file_operations fops = { .owner = THIS_MODULE, .open = test_open, .release = test_close, .read = test_read, .write = etst_write, .unlocked_ioctl = test_ioctl, }; ...... static long test_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { //printk("[%s]\n", __func__); int ret; struct msg my_msg; /* 檢查設備類型 */ if (_IOC_TYPE(cmd) != IOC_MAGIC) { pr_err("[%s] command type [%c] error!\n", __func__, _IOC_TYPE(cmd)); return -ENOTTY; } /* 檢查序數 */ if (_IOC_NR(cmd) > IOC_MAXNR) { pr_err("[%s] command numer [%d] exceeded!\n", __func__, _IOC_NR(cmd)); return -ENOTTY; } /* 檢查訪問模式 */ if (_IOC_DIR(cmd) & _IOC_READ) ret= !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); else if (_IOC_DIR(cmd) & _IOC_WRITE) ret= !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); if (ret) return -EFAULT; switch(cmd) { /* 初始化設備 */ case IOCINIT: init(); break; /* 讀寄存器 */ case IOCGREG: ret = copy_from_user(&msg, (struct msg __user *)arg, sizeof(my_msg)); if (ret) return -EFAULT; msg->data = read_reg(msg->addr); ret = copy_to_user((struct msg __user *)arg, &msg, sizeof(my_msg)); if (ret) return -EFAULT; break; /* 寫寄存器 */ case IOCWREG: ret = copy_from_user(&msg, (struct msg __user *)arg, sizeof(my_msg)); if (ret) return -EFAULT; write_reg(msg->addr, msg->data); break; default: return -ENOTTY; } return 0; }
用戶空間:
// ioctl-test.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include "ioctl-test.h" int main(int argc, char **argv) { int fd; int ret; struct msg my_msg; fd = open("/dev/ioctl-test", O_RDWR); if (fd < 0) { perror("open"); exit(-2); } /* 初始化設備 */ ret = ioctl(fd, IOCINIT); if (ret) { perror("ioctl init:"); exit(-3); } /* 往寄存器0x01寫入數據0xef */ memset(&my_msg, 0, sizeof(my_msg)); my_msg.addr = 0x01; my_msg.data = 0xef; ret = ioctl(fd, IOCWREG, &my_msg); if (ret) { perror("ioctl read:"); exit(-4); } /* 讀寄存器0x01 */ memset(&my_msg, 0, sizeof(my_msg)); my_msg.addr = 0x01; ret = ioctl(fd, IOCGREG, &my_msg); if (ret) { perror("ioctl write"); exit(-5); } printf("read: %#x\n", my_msg.data); return 0; }
四. netlink
1.簡介
借鑑博客:
https://blog.csdn.net/stone8761/article/details/72780863
2.應用接口
內核層操做
/*1.建立socket 參數: net: 通常直接填&init_net unit:協議類型,可自定義,如#define NETLINK_TEST 25 cfg:配置結構 */ static inline struct sock *
netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg); /*2.單播發送接口 參數: ssk:爲函數 netlink_kernel_create()返回的socket。 skb:存放消息,它的data字段指向要發送的netlink消息結構,而 skb的控制塊保存了消息的地址信息,宏NETLINK_CB(skb)就用於方便設置該控制塊。 portid:pid端口。 nonblock:表示該函數是否爲非阻塞,若是爲1,該函數將在沒有接收緩存可利用時當即返回;而若是爲0,該函數在沒有接收緩存可利用定時睡眠。 */ extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock); /*3.多播發送接口 參數: group:接收消息的多播組,該參數的每個位表明一個多播組,所以若是發送給多個多播組; allocation:內存分配類型,通常地爲GFP_ATOMIC或GFP_KERNEL,GFP_ATOMIC用於原子的上下文(即不能夠睡眠),而GFP_KERNEL用於非原子上下文。 */ extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 portid, __u32 group, gfp_t allocation); /*4.釋放socket 參數: */ extern void netlink_kernel_release(struct sock *sk);
用戶層操做
/*1.建立socket 參數: nlmsghdr結構常見操做: NLMSG_SPACE(len): 將len加上nlmsghdr頭長度,並按4字節對齊; NLMSG_DATA(nlh): 返回數據區首地址; */ int netlink_create_socket(void) { //create a socket return socket(AF_NETLINK, SOCK_RAW, NETLINK_TEST); } /*2.bind 參數: */ int netlink_bind(int sock_fd) { struct sockaddr_nl addr; memset(&addr, 0, sizeof(struct sockaddr_nl)); addr.nl_family = AF_NETLINK; addr.nl_pid = TEST_PID; addr.nl_groups = 0; return bind(sock_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_nl)); } /*3.發送接收*/ //使用sendmsg、recvmsg發送接收數據 ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); //使用sendto、recvfrom發送接收數據 ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
3.實例
#include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <string.h> #include <linux/netlink.h> #include <stdint.h> #include <unistd.h> #include <errno.h> #include <sys/poll.h> static void die(char *s) { write(2, s, strlen(s)); exit(1); } int main(int argc, char *argv[]) { struct sockaddr_nl nls; struct pollfd pfd; char buf[512]; //open hotplug event netlink socket memset(&nls, 0, sizeof(struct sockaddr_nl)); nls.nl_family = AF_NETLINK; nls.nl_pid = getpid(); nls.nl_groups = -1; pfd.events = POLLIN; pfd.fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); if (pfd.fd == -1) die("Not root\n"); //listen to netlink socket if (bind(pfd.fd, (void *)&nls, sizeof(struct sockaddr_nl))) die("Bind failed\n"); while(-1 != poll(&pfd, 1, -1)) { int i, len = recv(pfd.fd, buf, sizeof(buf), MSG_DONTWAIT); if (len == -1) die("recv\n"); //print the data to stdout i = 0; while (i < len) { printf("%s\n", buf + i); i += strlen(buf + i) + 1; } } die("poll\n"); return 0; }
編譯:gcc netlink_test.c -o netlink_test————————————————