Linux 字符设备驱动结构(一)—— cdev 结构体、设备号相关知识解析

一、字符设备基础知识

1、设备驱动分类

      linux系统将设备分为3类:字符设备、块设备、网络设备。使用驱动程序:



字符设备:是指只能一个字节一个字节读写的设备,不能随机读取设备内存中的某一数据,读取数据需要按照先后数据。字符设备是面向流的设备,常见的字符设备有鼠标、键盘、串口、控制台和LED设备等。

块设备:是指可以从设备的任意位置读取一定长度数据的设备。块设备包括硬盘、磁盘、U盘和SD卡等。

每一个字符设备或块设备都在/dev目录下对应一个设备文件linux用户程序通过设备文件(或称设备节点)来使用驱动程序操作字符设备和块设备


2、字符设备、字符设备驱动与用户空间访问该设备的程序三者之间的关系


     如图,在Linux内核中:

a -- 使用cdev结构体来描述字符设备;

b -- 通过其成员dev_t来定义设备号(分为主、次设备号)以确定字符设备的唯一性;

c -- 通过其成员file_operations来定义字符设备驱动提供给VFS的接口函数,如常见的open()、read()、write()等;


     在Linux字符设备驱动中:

a -- 模块加载函数通过 register_chrdev_region( ) 或 alloc_chrdev_region( )来静态或者动态获取设备号;

b -- 通过 cdev_init( ) 建立cdev与 file_operations之间的连接,通过 cdev_add( ) 向系统添加一个cdev以完成注册;

c -- 模块卸载函数通过cdev_del( )来注销cdev,通过 unregister_chrdev_region( )来释放设备号;


     用户空间访问该设备的程序:

a -- 通过Linux系统调用,如open( )、read( )、write( ),来“调用”file_operations来定义字符设备驱动提供给VFS的接口函数;


3、字符设备驱动模型



二、cdev 结构体解析

      在Linux内核中,使用cdev结构体来描述一个字符设备,cdev结构体的定义如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. <include/linux/cdev.h>  
  2.   
  3. struct cdev {   
  4.     struct kobject kobj;                  //内嵌的内核对象.  
  5.     struct module *owner;                 //该字符设备所在的内核模块的对象指针.  
  6.     const struct file_operations *ops;    //该结构描述了字符设备所能实现的方法,是极为关键的一个结构体.  
  7.     struct list_head list;                //用来将已经向内核注册的所有字符设备形成链表.  
  8.     dev_t dev;                            //字符设备的设备号,由主设备号和次设备号构成.  
  9.     unsigned int count;                   //隶属于同一主设备号的次设备号的个数.  
  10. };  

内核给出的操作struct cdev结构的接口主要有以下几个:

a -- void cdev_init(struct cdev *, const struct file_operations *);

其源代码如代码清单如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. void cdev_init(struct cdev *cdev, const struct file_operations *fops)  
  2. {  
  3.     memset(cdev, 0, sizeof *cdev);  
  4.     INIT_LIST_HEAD(&cdev->list);  
  5.     kobject_init(&cdev->kobj, &ktype_cdev_default);  
  6.     cdev->ops = fops;  
  7. }  
       该函数主要对struct cdev结构体做初始化 最重要的就是建立cdev 和 file_operations之间的连接:

(1) 将整个结构体清零;

(2) 初始化list成员使其指向自身;

(3) 初始化kobj成员;

(4) 初始化ops成员;


 b --struct cdev *cdev_alloc(void);

     该函数主要分配一个struct cdev结构动态申请一个cdev内存,并做了cdev_init中所做的前面3步初始化工作(第四步初始化工作需要在调用cdev_alloc后,显式的做初始化即: .ops=xxx_ops).

其源代码清单如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. struct cdev *cdev_alloc(void)  
  2. {  
  3.     struct cdev *p = kzalloc(sizeof(struct cdev), GFP_KERNEL);  
  4.     if (p) {  
  5.         INIT_LIST_HEAD(&p->list);  
  6.         kobject_init(&p->kobj, &ktype_cdev_dynamic);  
  7.     }  
  8.     return p;  
  9. }  

     在上面的两个初始化的函数中,我们没有看到关于owner成员、dev成员、count成员的初始化;其实,owner成员的存在体现了驱动程序与内核模块间的亲密关系,struct module是内核对于一个模块的抽象,该成员在字符设备中可以体现该设备隶属于哪个模块,在驱动程序的编写中一般由用户显式的初始化 .owner = THIS_MODULE, 该成员可以防止设备的方法正在被使用时,设备所在模块被卸载。而dev成员和count成员则在cdev_add中才会赋上有效的值。

 
c -- int cdev_add(struct cdev *p, dev_t dev, unsigned count);

       该函数向内核注册一个struct cdev结构,即正式通知内核由struct cdev *p代表的字符设备已经可以使用了。

当然这里还需提供两个参数:

(1)第一个设备号 dev,

(2)和该设备关联的设备编号的数量。

这两个参数直接赋值给struct cdev 的dev成员和count成员。


d -- void cdev_del(struct cdev *p);

     该函数向内核注销一个struct cdev结构,即正式通知内核由struct cdev *p代表的字符设备已经不可以使用了。

     从上述的接口讨论中,我们发现对于struct cdev的初始化和注册的过程中,我们需要提供几个东西

(1) struct file_operations结构指针;

(2) dev设备号;

(3) count次设备号个数。

但是我们依旧不明白这几个值到底代表着什么,而我们又该如何去构造这些值!



三、设备号相应操作

1 -- 主设备号和次设备号(二者一起为设备号):

      一个字符设备或块设备都有一个主设备号和一个次设备号。主设备号用来标识与设备文件相连的驱动程序,用来反映设备类型。次设备号被驱动程序用来辨别操作的是哪个设备,用来区分同类型的设备。

  linux内核中,设备号用dev_t来描述,2.6.28中定义如下:

  typedef u_long dev_t;

  在32位机中是4个字节,高12位表示主设备号,低20位表示次设备号。

内核也为我们提供了几个方便操作的宏实现dev_t:

1) -- 从设备号中提取major和minor

MAJOR(dev_t dev);                              

MINOR(dev_t dev);

2) -- 通过major和minor构建设备号

MKDEV(int major,int minor);

注:这只是构建设备号。并未注册,需要调用 register_chrdev_region 静态申请;

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. //宏定义:  
  2. #define MINORBITS    20  
  3. #define MINORMASK    ((1U << MINORBITS) - 1)  
  4. #define MAJOR(dev)    ((unsigned int) ((dev) >> MINORBITS))  
  5. #define MINOR(dev)    ((unsigned int) ((dev) & MINORMASK))  
  6. #define MKDEV(ma,mi)    (((ma) << MINORBITS) | (mi))</span>  


2、分配设备号(两种方法):

a -- 静态申请

int register_chrdev_region(dev_t from, unsigned count, const char *name);

其源代码清单如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. int register_chrdev_region(dev_t from, unsigned count, const char *name)  
  2. {  
  3.     struct char_device_struct *cd;  
  4.     dev_t to = from + count;  
  5.     dev_t n, next;  
  6.   
  7.     for (n = from; n < to; n = next) {  
  8.         next = MKDEV(MAJOR(n)+1, 0);  
  9.         if (next > to)  
  10.             next = to;  
  11.         cd = __register_chrdev_region(MAJOR(n), MINOR(n),  
  12.                    next - n, name);  
  13.         if (IS_ERR(cd))  
  14.             goto fail;  
  15.     }  
  16.     return 0;  
  17. fail:  
  18.     to = n;  
  19.     for (n = from; n < to; n = next) {  
  20.         next = MKDEV(MAJOR(n)+1, 0);  
  21.         kfree(__unregister_chrdev_region(MAJOR(n), MINOR(n), next - n));  
  22.     }  
  23.     return PTR_ERR(cd);  
  24. }  

b -- 动态分配:

int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);

其源代码清单如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,  
  2.             const char *name)  
  3. {  
  4.     struct char_device_struct *cd;  
  5.     cd = __register_chrdev_region(0, baseminor, count, name);  
  6.     if (IS_ERR(cd))  
  7.         return PTR_ERR(cd);  
  8.     *dev = MKDEV(cd->major, cd->baseminor);  
  9.     return 0;  
  10. }  

可以看到二者都是调用了__register_chrdev_region 函数,其源代码如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static struct char_device_struct *  
  2. __register_chrdev_region(unsigned int major, unsigned int baseminor,  
  3.                int minorct, const char *name)  
  4. {  
  5.     struct char_device_struct *cd, **cp;  
  6.     int ret = 0;  
  7.     int i;  
  8.   
  9.     cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);  
  10.     if (cd == NULL)  
  11.         return ERR_PTR(-ENOMEM);  
  12.   
  13.     mutex_lock(&chrdevs_lock);  
  14.   
  15.     /* temporary */  
  16.     if (major == 0) {  
  17.         for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i--) {  
  18.             if (chrdevs[i] == NULL)  
  19.                 break;  
  20.         }  
  21.   
  22.         if (i == 0) {  
  23.             ret = -EBUSY;  
  24.             goto out;  
  25.         }  
  26.         major = i;  
  27.         ret = major;  
  28.     }  
  29.   
  30.     cd->major = major;  
  31.     cd->baseminor = baseminor;  
  32.     cd->minorct = minorct;  
  33.     strlcpy(cd->name, name, sizeof(cd->name));  
  34.   
  35.     i = major_to_index(major);  
  36.   
  37.     for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)  
  38.         if ((*cp)->major > major ||  
  39.             ((*cp)->major == major &&  
  40.              (((*cp)->baseminor >= baseminor) ||  
  41.               ((*cp)->baseminor + (*cp)->minorct > baseminor))))  
  42.             break;  
  43.   
  44.     /* Check for overlapping minor ranges.  */  
  45.     if (*cp && (*cp)->major == major) {  
  46.         int old_min = (*cp)->baseminor;  
  47.         int old_max = (*cp)->baseminor + (*cp)->minorct - 1;  
  48.         int new_min = baseminor;  
  49.         int new_max = baseminor + minorct - 1;  
  50.   
  51.         /* New driver overlaps from the left.  */  
  52.         if (new_max >= old_min && new_max <= old_max) {  
  53.             ret = -EBUSY;  
  54.             goto out;  
  55.         }  
  56.   
  57.         /* New driver overlaps from the right.  */  
  58.         if (new_min <= old_max && new_min >= old_min) {  
  59.             ret = -EBUSY;  
  60.             goto out;  
  61.         }  
  62.     }  
  63.   
  64.     cd->next = *cp;  
  65.     *cp = cd;  
  66.     mutex_unlock(&chrdevs_lock);  
  67.     return cd;  
  68. out:  
  69.     mutex_unlock(&chrdevs_lock);  
  70.     kfree(cd);  
  71.     return ERR_PTR(ret);  
  72. }  
 通过这个函数可以看出  register_chrdev_region  alloc_chrdev_region  的区别,register_chrdev_region直接将Major 注册进入,而 alloc_chrdev_region从Major = 0 开始,逐个查找设备号,直到找到一个闲置的设备号,并将其注册进去;

二者应用可以简单总结如下:

                                     register_chrdev_region                                                alloc_chrdev_region 

devno = MKDEV(major,minor);
ret = register_chrdev_region(devno, 1, "hello"); 
cdev_init(&cdev,&hello_ops);
ret = cdev_add(&cdev,devno,1);
alloc_chrdev_region(&devno, minor, 1, "hello");
major = MAJOR(devno);
cdev_init(&cdev,&hello_ops);
ret = cdev_add(&cdev,devno,1)
register_chrdev(major,"hello",&hello

     可以看到,除了前面两个函数,还加了一个register_chrdev 函数,可以发现这个函数的应用非常简单,只要一句就可以搞定前面函数所做之事;

下面分析一下register_chrdev 函数,其源代码定义如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static inline int register_chrdev(unsigned int major, const char *name,  
  2.                   const struct file_operations *fops)  
  3. {  
  4.     return __register_chrdev(major, 0, 256, name, fops);  
  5. }  
调用了 __register_chrdev(major, 0, 256, name, fops) 函数:
[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. int __register_chrdev(unsigned int major, unsigned int baseminor,  
  2.               unsigned int count, const char *name,  
  3.               const struct file_operations *fops)  
  4. {  
  5.     struct char_device_struct *cd;  
  6.     struct cdev *cdev;  
  7.     int err = -ENOMEM;  
  8.   
  9.     cd = __register_chrdev_region(major, baseminor, count, name);  
  10.     if (IS_ERR(cd))  
  11.         return PTR_ERR(cd);  
  12.   
  13.     cdev = cdev_alloc();  
  14.     if (!cdev)  
  15.         goto out2;  
  16.   
  17.     cdev->owner = fops->owner;  
  18.     cdev->ops = fops;  
  19.     kobject_set_name(&cdev->kobj, "%s", name);  
  20.   
  21.     err = cdev_add(cdev, MKDEV(cd->major, baseminor), count);  
  22.     if (err)  
  23.         goto out;  
  24.   
  25.     cd->cdev = cdev;  
  26.   
  27.     return major ? 0 : cd->major;  
  28. out:  
  29.     kobject_put(&cdev->kobj);  
  30. out2:  
  31.     kfree(__unregister_chrdev_region(cd->major, baseminor, count));  
  32.     return err;  
  33. }  
可以看到这个函数不只帮我们注册了设备号,还帮我们做了cdev 的初始化以及cdev 的注册;

3、注销设备号:

void unregister_chrdev_region(dev_t from, unsigned count);


4、创建设备文件:

     利用cat /proc/devices查看申请到的设备名,设备号。

1)使用mknod手工创建:mknod filename type major minor

2)自动创建设备节点:

    利用udev(mdev)来实现设备文件的自动创建,首先应保证支持udev(mdev),由busybox配置。在驱动初始化代码里调用class_create为该设备创建一个class,再为每个设备调用device_create创建对应的设备。

    详细解析见:Linux 字符设备驱动开发 (二)—— 自动创建设备节点


下面看一个实例,练习一下上面的操作:

hello.c

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. #include <linux/module.h>  
  2. #include <linux/fs.h>  
  3. #include <linux/cdev.h>  
  4. static int major = 250;  
  5. static int minor = 0;  
  6. static dev_t devno;  
  7. static struct cdev cdev;  
  8. static int hello_open (struct inode *inode, struct file *filep)  
  9. {  
  10.     printk("hello_open \n");  
  11.     return 0;  
  12. }  
  13. static struct file_operations hello_ops=  
  14. {  
  15.     .open = hello_open,           
  16. };  
  17.   
  18. static int hello_init(void)  
  19. {  
  20.     int ret;      
  21.     printk("hello_init");  
  22.     devno = MKDEV(major,minor);  
  23.     ret = register_chrdev_region(devno, 1, "hello");  
  24.     if(ret < 0)  
  25.     {  
  26.         printk("register_chrdev_region fail \n");  
  27.         return ret;  
  28.     }  
  29.     cdev_init(&cdev,&hello_ops);  
  30.     ret = cdev_add(&cdev,devno,1);  
  31.     if(ret < 0)  
  32.     {  
  33.         printk("cdev_add fail \n");  
  34.         return ret;  
  35.     }     
  36.     return 0;  
  37. }  
  38. static void hello_exit(void)  
  39. {  
  40.     cdev_del(&cdev);  
  41.     unregister_chrdev_region(devno,1);  
  42.     printk("hello_exit \n");  
  43. }  
  44. MODULE_LICENSE("GPL");  
  45. module_init(hello_init);  
  46. module_exit(hello_exit);  

测试程序 test.c

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. #include <sys/types.h>  
  2. #include <sys/stat.h>  
  3. #include <fcntl.h>  
  4. #include <stdio.h>  
  5.   
  6. main()  
  7. {  
  8.     int fd;  
  9.   
  10.     fd = open("/dev/hello",O_RDWR);  
  11.     if(fd<0)  
  12.     {  
  13.         perror("open fail \n");  
  14.         return ;  
  15.     }  
  16.   
  17.     close(fd);  
  18. }  
makefile:
[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. ifneq  ($(KERNELRELEASE),)  
  2. obj-m:=hello.o  
  3. $(info "2nd")  
  4. else  
  5. KDIR := /lib/modules/$(shell uname -r)/build  
  6. PWD:=$(shell pwd)  
  7. all:  
  8.     $(info "1st")  
  9.     make -C $(KDIR) M=$(PWD) modules  
  10. clean:  
  11.     rm -f *.ko *.o *.symvers *.mod.c *.mod.o *.order  
  12. endif  

编译成功后,使用 insmod 命令加载:

然后用cat /proc/devices 查看,会发现设备号已经申请成功;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402119.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux 驱动开发之内核模块开发(四)—— 符号表的导出

Linux内核头文件提供了一个方便的方法用来管理符号的对模块外部的可见性,因此减少了命名空间的污染(命名空间的名称可能会与内核其他地方定义的名称冲突),并且适当信息隐藏。 如果你的模块需要输出符号给其他模块使用,应当使用下面的宏定义: EXPORT_SYMBOL(name); EXPORT_SYMBO…

Linux 驱动开发之内核模块开发 (三)—— 模块传参

一、module_param() 定义 通常在用户态下编程&#xff0c;即应用程序&#xff0c;可以通过main()的来传递命令行参数&#xff0c;而编写一个内核模块&#xff0c;则通过module_param() 来传参。 module_param()宏是Linux 2.6内核中新增的&#xff0c;该宏被定义在include/linux…

Exynos4412 文件系统制作(二)—— 文件系统简介

一、Linux磁盘分区和目录 Linux发行版本之间的差别很少&#xff0c;差别主要表现在系统管理的特色工具以及软件包管理方式的不同。目录结构基本上都是一样的。 Windows的文件结构是多个并列的树状结构&#xff0c;最顶部的是不同的磁盘&#xff08;分区&#xff09;&#xff0c…

MM引擎新应用——爱车加油记

基于MM应用引擎开发的EXTJS应用&#xff0c;车主每次加完汽油后&#xff0c;记录加油时的里程数以及加油金额和汽油价格&#xff0c;就可计算出上次加油后行驶的里程数、上次加油的平均油耗。点击刷新按钮&#xff0c;即可计算有记录以来的行驶公里数和再次期间加油金额和平均油…

### 阅读之痕-2013/11

2019独角兽企业重金招聘Python工程师标准>>> 阅读之痕-2013/11 Andy erpingwugmail.com 2013/11/01-2013/11/30 2013/11/01-2013/11/30 The story of rocksdb - Embedded key-value store for Flash and RAM http://rocksdb.org/intro.pdf High Performance Network…

Exynos4412 内核移植(六)—— 设备树解析

一、描述ARM Device Tree起源于OpenFirmware (OF)&#xff0c;在过去的Linux中&#xff0c;arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码&#xff0c;相当多数的代码只是在描述板级细节&#xff0c;而这些板级细节对于内核来讲&#xff0c;不过是垃圾&#xff…

Github Page创建个人主页以及绑定域名

2019独角兽企业重金招聘Python工程师标准>>> 在github中 我们可以通过github page创建个人主页 以及绑定域名 据说有300m空间 以及无限流量 不过只能支持静态html 以及一些脚本语言 顺便吐槽一下 阿里云最低配那个云空间服务器 512m内存 启动web服务器后 mys…

Exynos4412 内核移植(五)—— 驱动的移植

以移植自己制作的驱动&#xff0c;学习内核移植中的驱动移植&#xff0c;及 驱动程序的动态编译和静态编译 硬件环境&#xff1a; Linux 内核版本&#xff1a;Linux 3.14 主机&#xff1a;Ubuntu 12.04发行版 目标机&#xff1a;FS4412平台 交叉编译工具&#xff1a;arm-none-l…

FTP文件共传输服务

FTP文件共传输服务一&#xff0c;vsftpd服务基础&#xff08;1&#xff09;&#xff0c;FTP服务概述FTP&#xff08;File Transfer Protocol&#xff0c;文件传输协议&#xff09;是典型的C/S结构的应用层协议&#xff0c;需要由服务端软件、客户端软件共同实现文件传达输功能…

Exynos4412 内核移植(四)—— MMU 相关知识解析

一、MMU的产生 许多年以前&#xff0c;当人们还在使用DOS或是更古老的操作系统的时候&#xff0c;计算机的内存还非常小&#xff0c;一般都是以K为单位进行计算&#xff0c;相应的&#xff0c;当时的程序规模也不大&#xff0c;所以内存容量虽然小&#xff0c;但还是可以容纳当…

Mysql limit 子查询

为什么80%的码农都做不了架构师&#xff1f;>>> &#xff08;1&#xff09;mysql limit 不支持子查询像下面这条语句无法执行 SELECT * FROM b_c_s1 where CT_ID IN (SELECT DISTINCT CT_ID FROM b_c_s1 LIMIT 0,2); &#xff08;2&#xff09;应该在只查询外面再…

Exynos4412 内核移植(二)—— 内核编译过程分析

内核的编译同样是从Makefile 来分析&#xff1a; 一、内核源码结构 Linux内核文件数目近2万&#xff0c;出去其他架构CPU的相关文件&#xff0c;他们分别位于顶层目录下的17个子目录&#xff0c;各个目录功能独立&#xff0c;下面是常用目录&#xff1a; arch:体系结构相关代码…

深入理解Java:注解(Annotation)

2019独角兽企业重金招聘Python工程师标准>>> 一、概述 1.什么是注解&#xff08;Annotation&#xff09; Annotation&#xff08;注解&#xff09;就是Java提供了一种元程序中的元素关联任何信息和着任何元数据&#xff08;metadata&#xff09;的途径和方法。Ann…

Exynos4412 内核移植(三)—— 内核启动过程分析

内核启动所用函数如下&#xff1a; 与移植U-Boot 的过程相似&#xff0c;在移植Linux 之前&#xff0c;先了解它的启动过程。Linux 的过程可以分为两部分&#xff1a;架构/开发板相关的引导过程、后续的通用启动过程。对于uImage、zImage ,它们首先进行自解压得到vmlinux &…

开源自己用python封装的一个Windows GUI(UI Automation)自动化工具,支持MFC,Windows Forms,WPF,Metro,Qt...

首先&#xff0c;大家可以看下这个链接 Windows GUI自动化测试技术的比较和展望 。 这篇文章介绍了Windows中GUI自动化的三种技术&#xff1a;Windows API, MSAA - Microsoft Active Accessibility, UIAutomation 用脚本语言AutoIT实现自动化就是第一种技术Windows API, 查找窗…

Exynos4412 Uboot 移植(六)—— 相关知识补充

Uboot版本&#xff1a;u-boot-2013.01 一、gd结构体的定义与使用 gd_t 和 bd_t 是u-boot中两个重要的数据结构&#xff0c;在初始化操作很多都要靠这两个数据结构来保存或传递。 gd_t 定义在/u-boot-2013.01/arch/arm/include/asm/global_data.h bd_t 定义在 ./include/asm-ar…

Exynos4412 Uboot 移植(五)—— Uboot 移植过程

Uboot 版本&#xff1a;u-boot-2013.01 开发板&#xff1a;FS_4412 平台&#xff08;Exynos4412,可以根据自己的板子修改&#xff0c;只要是4412的过程都是一样的&#xff09; 一、建立自己的平台 1、下载源码 我们可以在下面这个网站上下载最新的和以前任一版本的uboot ftp://…

Nagios 安装及常见错误

一、实验环境监控服务器&#xff08;nagios服务器--192.168.1.100&#xff09;CentOS5.4 nagios-3.2.1 nagios-plugins-1.4.14 nrpe-2.12被监控客户端&#xff08;linux客户端--192.168.1.200&#xff09;CentOS5.4 nagios-plugins-1.4.14 nrpe-2.12二、nrpe插件1、nrpe插…

Exynos4412 Uboot 移植(四)—— Uboot引导内核过程分析

bootloader 要想启动内核&#xff0c;可以直接跳到内核的第一个指令处&#xff0c;即内核的起始地址&#xff0c;这样便可以完成内核的启动工作了。但是要想启动内核还需要满足一些条件&#xff0c;如下所示&#xff1a; 1、cpu 寄存器设置 * R0 0 * R1 机器类型 id …