操作系统实验三文件系统和设备管理Word文档格式.docx
《操作系统实验三文件系统和设备管理Word文档格式.docx》由会员分享,可在线阅读,更多相关《操作系统实验三文件系统和设备管理Word文档格式.docx(12页珍藏版)》请在冰豆网上搜索。
了解LINUX下文件系统和I/O的实现,了解其中模块的设计。
2.项目要求
(a)实现一个LINUX下串口的非阻塞读写程序。
(b)了解LINUX下模块的实现方式,及相应的编译和安装及卸载过程。
实现一个模块,在模块安装时可打印信息(Hello)和卸载时打印信息(Goodbye).
(c)实现一个procfs文件系统的模块。
3.具体实现
3.1LINUX下串口的非阻塞读写程序
在Linux下串口文件是位于/dev下,串口一为/dev/ttyS0,串口二为/dev/ttyS1打开串口是通过使用标准的文件打开函数操作:
intfd;
/*以读写方式打开串口*/
fd=open("
/dev/ttyS0"
O_RDWR);
if(-1==fd){
/*不能打开串口一*/
perror("
提示错误!
"
);
}
在使用时还应当设置串口、读写串口、关闭串口等,通过查阅资料实现了对串口的读写
代码见附录
3.2模块
模块是具有独立功能的程序,它可以被单独编译,但不能独立运行。
它在运行时被链接到内核作为内核的一部分在内核空间运行,这与运行在用户空间的进程是不同的。
模块通常由一组函数和数据结构组成,用来实现一种文件系统、一个驱动程序或其他内核上层的功能。
首先在电脑里编写一段C语言代码,hello.c。
代码如下
分析:
(1)在内核模块的开始一部分,跟C语言的一般程序一样,是模块所需要的头文件。
(2)模块许可证声明,这部分是必须有的。
模块许可证(LICENSE)声明描述内核模块的许可权限,如果不声明LICENSE,模块被加载时,将收到内核被污染(kerneltainted)的警告。
大多数情况下,内核模块应遵守GPL兼容许可权。
Linux2.6内核模块最常见的是以MODULE_LICENSE("
DualBSD/GPL"
)语句声明模块采用BSD/GPL双LICENSE。
(3)模块加载函数,这部分是必须的。
模块加载函数必须“module_init(函数名)“的形式被指定。
它返回整形值,若初始化成功,应返回0。
在上面那个例子当中,hello_init()函数就是模块加载函数需要执行的,主要是打印一条信息。
(4)跟模块加载函数相对应的就是模块卸载函数,这部分也是必须的。
模块卸载函数在模块卸载的时候执行,不返回任何值,必须以“module_exit(函数名)“的形式来指定。
在上面的例子中,hello_exit()函数就是模块卸载函数需要执行的,只要是打印了一条退出信息。
(5)函数最后的一部分,是模块声明与描述部分。
这部分可有可无。
在Linux内核模块中,我们可以用
MODULE_AUTHOR,MODULE_DESCRIPTION,MODULE_VERSION,MODULE_DEVICE_TABLE,MODULE_ALIAS分别声明模块的作者、描述、版本、设备表和别名。
(6)module_init()是驱动程序初始化的入口点。
它就相当于c语言程序中的main()函数。
对于内置的模块,内核在引导时调用该引导点,对于可加载模块则在模块插入到内核时才调用。
(7)模块加载函数和模块卸载函数中都用到了printk()函数,该函数是由内核定义的,功能与C库中的printf()类似,它把要打印的信息输出到终端或系统日志中。
其次,编写Makefile文件,通过它来管理一个庞大的项目是再好不过的。
下面我们就在刚才.c文件目录下编写一个Makefile文件。
对应的代码如下:
pwd是获得当前的相对路径,然后就是获得当前的内核版本号,我们可以用uname-r命令,这样我们就获得了当前内核的绝对路径。
这样做的一个好处,就是你可以在不同的内核版本中进行移植,而且可读性也增强了。
有了Makefile文件后,就离成功不远了。
在.c文件的同一目录下,执行make命令,系统会在当前目录下生成各种文件。
其中就有与之相关的.o和.ko文件。
hello.ko就是模块目标文件。
到此,模块编译好了。
截图如下:
模块编译好了,但是还不能为我们工作。
下面就是将目标模块插入到内核和从内核中删除。
这里需要用到两个命令,insmod和rmmod我们光看这两个命令单词就能猜出他们的意思。
输入命令:
sudoinsmodhello.ko。
要看信息必须要进入到日志文件中,可输入命令进到系统日志:
dmesg,会发现有一条信息,HelloWorld,说明我们刚才编写的模块已经插入到内核当中了。
接下来输入命令:
sudormmodhello.ko。
再输入命令打开系统日志,我们会发现在会有一个新的信息Goodbye,这说明我们的模块卸载成功。
至此,实现一个模块,在模块安装时可打印信息(Helloworld)和卸载时打印信息(Goodbye)完成
3.3实现一个procfs文件系统的模块
/proc文件系统是在LINUX系统中的一个特殊部分,它并不属于任何应用的文件系统,它的主要作用是用于用户与系统的参数交换,但使用更多的是通过/proc系统中的文件内容来查看系统当前运行状态。
程序中主要是要靠调用LINUX提供的系统函数来创建表项和文件夹,并且通过修改读写函数的指针,可以实现对于该文件读写的重调用。
其中,建立文件和文件夹的函数是:
staticstructproc_dir_entry*mydir;
staticstructproc_dir_entry*pfile;
staticintmyproc_write(structfile*,constchar__user*,
unsignedlong,void*)
staticintmyproc_read(char*,char**,off_t,
int,int*,void*)
编写Makefile文件,和上面的类似,这里就不过多赘述。
编写编译procfc_模块,:
生成如下文件:
在/procfc_os中创建mydir文件夹
4实验心得
(a)在实验中初步学习了LINUX串口编程,了解了各个函数的功能。
因为硬件的关系,故只是模拟了串口的写入及读出。
实际中应该将两个串口相连来实现
(b)本次实验中亲自动手实践了LINUX内核的模块编程,并初步了解了内核模块在LINUX内核中的位置和运行方式。
加强了对linux模块的了解
(c)本次实验中结合内核模块的编程方式,通过上网查找资料及去图书馆借阅书籍,学会写PROC文件系统的文件。
5附录(3.1及3.3的原程序)
5.1
#include<
stdio.h>
sys/types.h>
sys/stat.h>
fcntl.h>
termios.h>
intmain()
{
intfd,flag,wr_num=0,rd_num=0;
charsend_buf[512],recv_buf[512];
while
(1)
{
fd=open("
/dev/minttyS0"
O_RDWR|O_NOCTTY|O_NDELAY);
if(fd==-1)
printf("
cannotopentheCOM1!
\n"
else
{
fcntl(fd,F_SETFL,0);
openInput-serialportsuccessfully!
Input:
scanf("
%s"
send_buf);
wr_num=write(fd,send_buf,sizeof(send_buf));
/*先写入*/
if(wr_num>
0)
writesuccess!
\n\n"
writefail!
sleep
(1);
}
close(fd);
cannotopentheCOM2!
openOutput-serialportsuccessfully!
rd_num=read(fd,recv_buf,sizeof(recv_buf));
/*再读出*/
if(rd_num>
recv_buf[rd_num]='
\0'
;
wecanread\"
%s\"
fromtheCOM1.total:
%dcharacters\n\n"
recv_buf,rd_num);
}
readfail!
sleep
(2);
5.2
#include<
linux/module.h>
linux/proc_fs.h>
asm/uaccess.h>
staticcharmsg[255];
staticintmyproc_read(char*page,char**start,off_toff,intcount,int*eof,void*data)
intlen=strlen(msg);
if(off>
=len)
return0;
if(count>
len-off)
count=len-off;
memcpy(page+off,msg+off,count);
returnoff+count;
staticintmyproc_write(structfile*file,constchar__user*buffer,unsignedlongcount,void*data)
unsignedlongcount2=count;
if(count2>
=sizeof(msg))
count2=sizeof(msg)-1;
if(copy_from_user(msg,buffer,count2))
return-EFAULT;
msg[count2]='
returncount;
staticint__initmyproc_init(void)
mydir=proc_mkdir("
mydir"
NULL);
if(!
mydir){
printk(KERN_ERR"
Can'
tcreate/procfc_os/mydir\n"
return-1;
pfile=create_proc_entry("
Pool"
0666,mydir);
pfile){
tcreate/procfc_os/mydir/pool\n"
remove_proc_entry("
pfile->
read_proc=myproc_read;
write_proc=myproc_write;
staticvoid__exitmyproc_exit(void)
pool"
mydir);