操作系统课程设计告学院:计算机科学与技术姓名班级:学号:姓名:学号:班级:一、实验目的 (2)二、实验任务 (2)三、实验步骤 (3)(一)安装Linux系统 (3)(二)重新编译内核 (6)(三)重新启动新内核 (7)(四)编译驱动程序 (8)四.心得体会 (19)一、实验目的1.掌握linux系统安装的方法2.掌握linux Kernel的编译过程3.掌握系统引导管理器Grub的使用4.学会编写简单的模块化的字符设备驱动程序二、实验任务1.在虚拟机下安装某一Linux的发行版(推荐CentOS)或者直接安装linux的一款系统2.从内核官网下载某一新的内核版本,并进行编译3.设置系统引导管理器(推荐Grub),使得系统可以从新内核启动4.编写一个简单的模块化字符设备驱动程序,动态地装载和卸载以模块形式存在的字符设备驱动程序。
同时,学会对所编写的字符设备驱动程序进行测试,最终了解Linux操作系统是如何管理字符设备的基本要求:(1)编写初始化和注销函数:scull_init() 和 scull_exit(); (2)编写5个基本的设备接口函数,包括scull_open(),scull_write(),scull_read(),scull_ioctl() (3)和scull_release();(4)一个测试程序来测试所编写的字符设备驱动程序。
三、实验步骤(一)安装Linux系统1.安装虚拟机:VMware workstation 102.新建虚拟机(1)导入安装程序光盘映像文件(导入Ubuntu 14.04压缩文件)(2)设置Ubuntu安装信息(4)随后会进入自动安装过程,直至出现虚拟机登录界面(二)重新编译内核1.下载安装文件首先启动Linux系统,并用户登录,进入终端模式下;(1)下载新的内核源代码linux-3.14.4.tar.xz,然后放在/usr/src 目录下;(2)开始解压新内核linux-3.14.4.tar.xz,具体命令如下:首先进入linux-3.14.4.tar.xz所在目录之下cd /usr/src然后进行解压命令:xz -d linux-3.14.4.tar.xz ;Tar –xvf linux-3.14.4.tar.2.编译内核在/usr/src/linux-3.14.4 目录下,执行以下命令即可编译:清除内核编译的目标文件:make clean内核编译:make bzImage(编译成功之后,会在/usr/src/linux/arch/i386/boot目录中生成一个新内核的映像文件bzImage。
)编译模块:make modules安装模块:make modules_install (安装成功后,系统会在/lib/modules目录下生成一个3.14.4子目录,里面存放着新内核的所有可加载模块)安装新内核:make install(三)重新启动新内核1.修改Grub启动管理器在/boot目录下修改grub配置文件,使用如下命令:在/boot下生成一个initrd-2.4.12.img,并且对grub.conf也作出相应更改:new-kernel-pkg --mkinitrd --depmod --install 3.14.4 查看根目录在那个分区:df进入grub.conf文件:vi /boot/grub/grub.conf随后查找如下信息:default=1timeout=10splashimage=(hd0,0)/grub/splash.xpm.gztitle Red Hat Linux (2.6.18)root (hd0,0)kernel /vmlinuz-2.6.18 ro root= LABEL=/initrd /initrd-2.6.18.img做出如下修改:(1)将default=1改为default=0(如果不做修改的话重启之后默认进入原先的内核)(2)将kernel行的“LABEL=/”换成根目录所在的分区,即“/dev/sda2”2.重启内核分别使用如下命令重启内核:Sudo update-grub 和 sudo reboot这样就可以重启了,在重启动过程中,按shift键显示新旧内核选择窗口,选择新安装的内核就可以登陆了。
(四)编译驱动程序Linux_2.6字符设备驱动实例第一步:my74hc595.c#include <linux/module.h> //模块所需的大量符号和函数定义#include <linux/init.h> //指定初始化和清除函数#include <linux/fs.h> //文件系统相关的函数和头文件#include <linux/cdev.h> //cdev结构的头文件#include <asm/uaccess.h> //在内核和用户空间中移动数据的函数#include <linux/slab.h>#include <linux/device.h>MODULE_LICENSE("GPL"); //指定代码使用的许可证//文件操作函数的声明int my74hc595_open(struct inode *, struct file *);int my74hc595_release(struct inode *, struct file *);ssize_t my74hc595_read(struct file *, char *, size_t, loff_t *);ssize_t my74hc595_write(struct file *, const char *, size_t, loff_t *);int dev_major = 1253; //指定主设备号int dev_minor = 0; //指定次设备号static struct class *firstdrv_class;static struct device *firstdrv_class_dev;struct cdev *my74hc595_cdev; //内核中表示字符设备的结构int *gp_testdata;//测试用数据struct file_operations my74hc595_fops= //将文件操作与分配的设备号相连{owner: THIS_MODULE, //指向拥有该模块结构的指针open: my74hc595_open,release: my74hc595_release,read: my74hc595_read,write: my74hc595_write,};static void __exit my74hc595_exit(void) //退出模块时的操作{dev_t devno=MKDEV(dev_major, dev_minor); //dev_t是用来表示设备编号的结构cdev_del(my74hc595_cdev); //从系统中移除一个字符设备kfree(my74hc595_cdev); //释放自定义的设备结构kfree(gp_testdata);unregister_chrdev_region(devno, 1); //注销已注册的驱动程序device_unregister(firstdrv_class_dev); //删除/dev下对应的字符设备节点class_destroy(firstdrv_class);printk("my74hc595 unregister success\n");}static int __init my74hc595_init(void) //初始化模块的操作{int ret, err;dev_t devno;#if 1//动态分配设备号,次设备号已经指定ret=alloc_chrdev_region(&devno, dev_minor, 1, "my74hc595");//保存动态分配的主设备号dev_major=MAJOR(devno);#else//根据期望值分配设备号devno=MKDEV(dev_major, dev_minor);ret=register_chrdev_region(devno, 1, "my74hc595");#endifif(ret<0){printk("my74hc595 register failure\n");//my74hc595_exit(); //如果注册设备号失败就退出系统return ret;else{printk("my74hc595 register success\n");}gp_testdata = kmalloc(sizeof(int), GFP_KERNEL);#if 0//两种初始化字符设备信息的方法my74hc595_cdev = cdev_alloc();//调试时,此中方法在rmmod 后会出现异常,原因未知my74hc595_cdev->ops = &my74hc595_fops;#elsemy74hc595_cdev = kmalloc(sizeof(struct cdev), GFP_KERNEL);cdev_init(my74hc595_cdev, &my74hc595_fops);#endifmy74hc595_cdev->owner = THIS_MODULE; //初始化cdev中的所有者字段err=cdev_add(my74hc595_cdev, devno, 1); //向内核添加这个cdev结构的信息if(err<0)printk("add device failure\n"); //如果添加失败打印错firstdrv_class = class_create(THIS_MODULE, "my74hc595");firstdrv_class_dev = device_create(firstdrv_class, NULL, MKDEV(dev_major, 0), NULL,"my74hc595-%d", 0);//在/dev下创建节点printk("register my74hc595 dev OK\n");return 0;}//打开设备文件系统调用对应的操作int my74hc595_open(struct inode *inode, struct file *filp) {//将file结构中的private_data字段指向已分配的设备结构filp->private_data = gp_testdata;printk("open my74hc595 dev OK\n");return 0;}//关闭设备文件系统调用对应的操作int my74hc595_release(struct inode *inode, struct file *filp) {printk("close my74hc595 dev OK\n");return 0;}//读设备文件系统调用对应的操作ssize_t my74hc595_read(struct file *filp, char *buf, size_t len, loff_t *off){//获取指向已分配数据的指针unsigned int *p_testdata = filp->private_data;//将设备变量值复制到用户空间if(copy_to_user(buf, p_testdata, sizeof(int))){return -EFAULT;}printk("read my74hc595 dev OK\n");return sizeof(int); //返回读取数据的大小}//写设备文件系统调用对应的操作ssize_t my74hc595_write(struct file *filp, const char *buf, size_t len, loff_t *off){//获取指向已分配数据的指针unsigned int *p_testdata = filp->private_data;//从用户空间复制数据到内核中的设备变量if(copy_from_user(p_testdata, buf, sizeof(int))){return -EFAULT;}printk("write my74hc595 dev OK\n");return sizeof(int); //返回写数据的大小}module_init(my74hc595_init); //模块被装载时调用my74hc595_initmodule_exit(my74hc595_exit); //模块被卸载时调用my74hc595_exit第二步:Makefile按如下内容编写一个Makefile文件,然后输入make就可以开始自动编译了。