----华中科技大学计算机学院
操作系统课程设计
实 验 报 告
班 级:
学 号:
姓 名:
指导教师:
完成日期: 2010-04-02
一、实验目的:
1. 掌握Linux操作系统的使用方法;
2. 了解Linux系统内核代码结构;
3. 掌握实例操作系统的实现方法。
二、实验题目:
1. 掌握Linux操作系统的使用方法,包括键盘命令、系统调用;掌握在Linux下的编程环境。
1) 编一个C程序,其内容为实现文件拷贝的功能;
2) 编一个C程序,其内容为分窗口同时显示三个并发进程的运行结果。要求用到Linux下的图形库。
2. 掌握系统调用的实现过程,通过编译内核方法,增加一个新的系统调用。另编写一个应用程序,调用新增加的系统调用。实现的功能是:文件拷贝;
3. 掌握增加设备驱动程序的方法。通过模块方法,增加一个新的设备驱动程序,其功能可以简单。实现字符设备的驱动。
4. 了解和掌握/proc文件系统的特点和使用方法
(1)了解/proc文件的特点和使用方法
(2)监控系统状态,显示系统中若干部件使用情况
(3)用图形界面实现系统监控状态。
5. 设计并实现一个模拟的文件系统(选做)
三、实验步骤及源代码:
实验一
题目一:
源代码如下:
#include<dos.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
FILE *f1,*f2;
f1=fopen("d:\\source1.txt","r");
f2=fopen("d:\\source2.txt","w");
while(!feof(f1))
{
fputc(fgetc(f1),f2);
}
fclose(f1);
fclose(f2);
printf("拷贝完成!!\n");
}
创建文件copy.c,输入源代码。进入终端后使用编译命令:gcc copy2.c -o copy2 编译源代码。然后运行新生成文件./copy2,就会把source1.txt内的文字复制到source2.txt中(如果source2.txt不存在则会新建该文件)。结果如上图所示。
题目二:
源代码如下:
3.c:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <linux/sem.h>
int p1,p2;
int main (void)
{
if((p1=fork())==0)
{
execv("./gtk1",NULL);
}
else
{
if((p2=fork())==0)
{
execv("./gtk2",NULL);
}
else
{
if (fork()==0)
{
execv("./gtk3",NULL);
}
}
}
return 0;
}
gtk1.c:
#include <gtk/gtk.h>
/* 传到这个函数的数据被打印到标准输出 */
void callback( GtkWidget *widget, gpointer data )
{
g_print ("Process 1- %s was pressed\n", (char *) data);
}
/* 这个回调退出程序 */
gint delete_event( GtkWidget *widget,GdkEvent *event,gpointer data )
{
gtk_main_quit ();
return FALSE;
}
int main( int argc,char *argv[] )
{
GtkWidget *window;
GtkWidget *button;
GtkWidget *table;
gtk_init (&argc, &argv); /* 创建一个新窗口 */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
/* 设置窗口标题 */
gtk_window_set_title (GTK_WINDOW (window), "进程一");
/* 为delete_event设置一个立即退出GTK的处理函数。 */
g_signal_connect (G_OBJECT (window), "delete_event",
G_CALLBACK (delete_event), NULL);
/* 设置窗口的边框宽度。 */
gtk_container_set_border_width (GTK_CONTAINER (window), 20);
/* 创建一个2x2的表 */
table = gtk_table_new (2, 2, TRUE);
/* 将表放进主窗口 */
gtk_container_add (GTK_CONTAINER (window), table);
/* 创建第一个按钮 */
button = gtk_button_new_with_label ("button 1");
/* 当这个按钮被点击时,我们调用 "callback" 函数,并将一个
* 指向"button 1"的指针作为它的参数 */
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (callback), (gpointer) "button 1");
/* 将button 1插入表的左上象限(quadrant) */
gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 1, 0, 1);
gtk_widget_show (button);
/* 创建第二个按钮 */
button = gtk_button_new_with_label ("button 2");
/* 当这个按钮被点击时,我们调用 "callback" 函数,并将一个
* 指向"button 2"的指针作为它的参数 */
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (callback), (gpointer) "button 2");
/* 将button 2插入表的右上象限 */
gtk_table_attach_defaults (GTK_TABLE (table), button, 1, 2, 0, 1);
gtk_widget_show (button);
/* 创建"Quit"按钮 */
button = gtk_button_new_with_label ("Quit");
/* 当这个按钮被点击时,我们调用 "delete_event" 函数接着
* 程序就退出了 */
g_signal_connect (G_OBJECT (button), "clicked",
G_CALLBACK (delete_event), NULL);
/* 将退出按钮插入表的下面两个象限 */
gtk_table_attach_defaults (GTK_TABLE (table), button, 0, 2, 1, 2);
gtk_widget_show (button);
gtk_widget_show (table);
gtk_widget_show (window);
gtk_main ();
return 0;
}
gtk2.c和gtk3.c的源代码与gtk1.c的大致相同。
依照源代码分别将gtk1.c、gtk1.c、gtk1.c和3.c文件编写好。进入终端后分别编译这几个文件。编译gtk1.c的命令为:
gcc gtk1.c -o gtk2 `pkg-config --cflags --libs gtk+-2.0`
因为该程序使用了gtk编程,所以需要在后面加上参数`pkg-config --cflags --libs gtk+-2.0`。编译3.c的命令与题目一中程序一样为:
gcc 3.c -o 3
编译完成后会生成相应的两个可执行文件。最后输入./3即可执行main程序,所得结果如上图所示,一共建立了3个进程process1、 process2和 process3。点击各自的quit按钮即可关闭所在的进程。
实验二
源代码如下:
系统调用代码:
asmlinkage int sys_filecopy(char* sourceFile,char* distFile)
{
int f1,f2,n;
char buf[512];
mm_segment_t fs;
if((f1=sys_open(sourceFile,O_RDONLY,0)) ==-1){
printk("Can't open %s\n",sourceFile);
sys_exit(-1);
}
if((f2=sys_open(distFile,O_CREAT|O_WRONLY|O_TRUNC,0666))==-1){
printk("Can't creat %s,mode %o\n",distFile,0666);
sys_exit(-1);
}
fs=get_fs();
set_fs(get_ds());
while((n=sys_read(f1,buf,512))>0)
{ if(sys_write(f2,buf,n)!=n){
printk("write error on file %s",distFile);
sys_exit(-1);
}
}
set_fs(fs);
sys_close(f1);
sys_close(f2);
return 0;
filecopy.c(测试代码):
#include<linux/unistd.h>
#include</usr/src/linux-2.6.31.12/arch/x86/include/asm/unistd.h>
#include<stdio.h>
#include<sys/syscall.h>
#define __NR_filecopy 337
int main(int argc,char *argv[])
{
int i;
if(argc !=3)
printf("error!");
else
{
i=syscall(337,argv[0],argv[1]);
printf("ok!");
}
return 0;
}
实验步骤如下:
1.将内核包linux-2.6.31.12.tar解压到 /usr/src 下。
2.增加新的系统调用,修改sys.c。
使用命令sudo gedit /usr/src/linux-2.6.31.12/kernel/sys.c打开sys.c文件,将系统调用的代码加入该文件中。
3.修改系统调用表syscall_table_32.S::
使用命令 sudo gedit /usr/src/linux-2.6.31.12/arch/x86/kernel/syscall_table_32.S打开系统调用表,在最后加上 .long sys_filecopy
4.增加新系统调用号,修改unistd.h
使用命令 sudo gedit /usr/src/linux-2.6.31.12/arch/x86/include/unistd_32.h打开该文件,在其中添加:#define _ _NR_filecopy 337。
5.编译内核,依次输入以下命令:
sudo cd /usr/src/linux-2.6.31.12
sudo make menuconfig
sudo make bzImage
sudo make modules
sudo make modules_install
sudo cp arch/i386/boot/bzImage /boot/vmlinuz-2.6.31.12
sudo mkinitramfs -o initrd.img-2.6.31.12 2.6.31.12
6.修改grub:
使用命令sudo gedit /boot/grub/grub.cfg 打开grub.cfg文件,仿照文件中的格式加入下列代码:
menuentry "Ubuntu, Linux 2.6.31.12" {
insmod ntfs
set root=(hd0,8)
search --no-floppy --fs-uuid --set e67496277495fb0f
loopback loop0 /ubuntu/disks/root.disk
set root=(loop0)
linux /boot/vmlinuz-2.6.31.12 root=/dev/sda8 loop=/ubuntu/disks/root.disk ro quiet splash
initrd /boot/inird-2.6.31.12.img
7.重启,导入新的内核,测试。
重启后选择Ubuntu, Linux 2.6.31.12进入,编译写好的测试文件filecopy.c。在终端中输入gcc filecopy.c -o filecopy 编译源代码,然后输入./filecopy 1.txt 2.txt运行新生成文件,如果1.txt不存在,则会报错,若存在,则成功复制。运行结果如上图所示,所达成的功能与实验一相同。
实验三
源代码如下:
device.c(驱动程序代码):
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
MODULE_LICENSE("GPL");
#define MAJOR_NUM 147
static ssize_t d_read(struct file *, char *, size_t, loff_t*);
static ssize_t d_write(struct file *, const char *, size_t, loff_t*);
static ssize_t d_open(struct inode *,struct file *);
static ssize_t d_release(struct inode *,struct file *);
struct file_operations d_fops=
{
.read=d_read,
.write=d_write,
.open=d_open,
.release=d_release,
};
static char var[1024]="Device test:";
int counter=12;
static int __init device_init(void)
{
int result;
result = register_chrdev(MAJOR_NUM, "device", &d_fops);
if(result)
{
printk("device register failure");
}
else
{
printk("device register success");
}
return result;
}
static void __exit device_exit(void)
{
unregister_chrdev(MAJOR_NUM, "device");
printk("unloading the device...");
}
static ssize_t d_read(struct file *filp, char *buf, size_t len, loff_t *off)
{
if(copy_to_user(buf,var,1024*sizeof(char)))
{
return - EFAULT;
}
return sizeof(char);
}
static ssize_t d_write(struct file *filp,const char *buf,size_t len,loff_t *off)
{
if(copy_from_user(var+counter,buf,len*sizeof(char)))
{
return - EFAULT;
}
counter+=len;
return sizeof(char);
}
static ssize_t d_open(struct inode *inode,struct file *file)
{
printk(KERN_INFO "open it!\n");
return 0;
}
static ssize_t d_release(struct inode *inode,struct file *file)
{
printk(KERN_INFO "close it\n");
return 0;
}
module_init(device_init);
module_exit(device_exit);
Makefile(Makefile文件):
ifneq ($(KERNELRELEASE),)
#kbuild syntax.
mymodule-objs :=device.o
obj-m := device.o
else
PWD :=$(shell pwd)
KVER :=$(shell uname -r)
KDIR :=/lib/modules/$(KVER)/build
all:
$(MAKE) -C $(KDIR) M=$(PWD)
clean:
rm -f *.cmd *.o *.mod *.ko
endif
sdevice.c(测试程序代码):
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
main()
{
int fd;
char buffer[1024];
int num;
fd=open("/dev/device",O_RDWR,S_IRUSR|S_IWUSR);
if(fd!=-1)
{
read(fd,buffer,1024*sizeof(char));
printf("The device is %s\n",buffer);
printf("Please input the characters written to device:\n");
fgets(buffer,1024,stdin);
num=strlen(buffer)-1;
write(fd,buffer,num*sizeof(char));
read(fd,buffer,1024*sizeof(char));
printf("The device is %s\n",buffer);
close(fd);
}
else
{
printf("Device open failure\n");
}
}
首先依照源代码分别创建相应的3个文件并保存。其中device.c和Makefile要保存在一个文件夹下。
然后依次输入以下命令:
sudo make (此时在device.c所在文件夹内会生成数个文件)
sudo insmod -f device.ko
sudo cat /proc/devices
在出现的项目中寻找device项,记住其对应的设备号(本次为147)。
输入命令sudo mknod /dev/device c 147 0,其中c指创建字符设备,第一个参数为主设备号,第二个从设备号为0。
之后即可编译并执行测试程序了。调试过程及结果如下图所示:
运行调试程序时需使用命令sudo ./sdevice,否则就会出现上图中出现的错误提示。
最后使用命令sudo rmmod device即可卸载驱动。
四、心得体会:
此次课程设计在linux平台上进行的,还是充满了好奇的,当在同学的指导下完成部分实验时内心还是很开心的。
自己对程序设计不是很感兴趣,学得也不杂样,在余建同学的帮助下完成此次课程设计的,在同学的帮助下练习了实验的操作,不过通过实验还是收获了不少,第一次切身使用linux系统,在linux系统下还进行了许多其它操作,体会到了它与windows的很大不同,当用linux完成很多平时在windows下觉得很简单的任务时,觉得很惊奇。
实验操作中也学会了许多命令,了解了一些linux命令的使用方法。比如说许多命令前都要加上sudo来修改访问权限,否则系统就无法执行相关的指令,开始操作的时候总是忽略,造成错误,后来了解后才在后来的操作中没有犯同样的错误。
虽然此次课程设计没有认认真真地花花很大精力去做,但是还是收获了不少。