西电-Linux上机-Modern-Operating-Systems-现代操作系统-课件_第1页
西电-Linux上机-Modern-Operating-Systems-现代操作系统-课件_第2页
西电-Linux上机-Modern-Operating-Systems-现代操作系统-课件_第3页
西电-Linux上机-Modern-Operating-Systems-现代操作系统-课件_第4页
西电-Linux上机-Modern-Operating-Systems-现代操作系统-课件_第5页
已阅读5页,还剩53页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

操作系统课程设计标题添加点击此处输入相关文本内容点击此处输入相关文本内容总体概述点击此处输入相关文本内容标题添加点击此处输入相关文本内容实验内容2系统调用题目要求为Linux内核增加一个系统调用,并编写用户进程的程序来测试。要求该系统调用能够完成以下功能:1.该系统调用有1个int型参数,返回值为int。2.若参数大于0,则返回自己学号的最后一位。如学号为13091248

,则返回8。3.若参数小于或等于0,则返回自己的学号后三位。如学号为13091248,则返回248。系统调用系统调用内核为用户进程提供的服务;提供用户模式的进程和硬件设备的接口;保护对内核所管理的资源的访问,提高系统安全;提高程序的可移植性;Linux系统调用在内核中全部以“sys_”开头,如sys_fork,sys_exit.系统调用SYSCALL、libc、APIsys_fork()sys_write()

sys_...

…Kernelfork()libcAppcall

fork()/printf()/strlen()/…printf()

write()

strlen()

…用户空间内核空间Hardware系统调用实现include/asm/unistd.h中定义了所有系统调用的编号跳转表以系统调用编号为下标,大小由NR_syscalls确定[include/linux/sys.h]没有定义的跳转表项,一律指向函数sys_ni_syscall()/*kernel/sys.c*/{return

-ENOSYS;}增加系统调用1.

修改usr/src/linux/arch/i386/kernel/entry.S(S必须为大写),找Entry(sys_call_table)增加新的系统调用表项:修改前的代码:.long

SYMBOL_NAME(sys_ni_syscall).long

SYMBOL_NAME(sys_vfork)/*

streams2*//*

190

*//**

NOTE!!

...*/.rept

NR_syscalls

-190.long

SYMBOL_NAME(sys_ni_syscall)需要修改系统调用实现1.

修改usr/src/linux/arch/i386/kernel/entry.S,增加新的系统调用表项:修改后的代码:.long

SYMBOL_NAME(sys_ni_syscall)/*

streams2

*/190.longSYMBOL_NAME(sys_vfork)/*

190*/.longSYMBOL_NAME(sys_mycall)/*191*//**

NOTE!!

...*/.rept

NR_syscalls

-

191.long

SYMBOL_NAME(sys_ni_syscall)课设题目的系统调用函数题目要求的系统调用函数可能形如asmlinkage

int

sys_mycall(int

num)

{if(num

>

0){

return

2;}else

{

return

13081032;}}系统调用实现3.

修改usr/include/asm/unistd.h,增加新的系统调用编号:#define

NR_mycall

191 /*

my

syscall

*/4.

通过编译产生新内核,并重启操作系统。Linux内核的构建确保是root用户cd

/usr/src/linux依次执行以下make语句make

mrpropermakeclean文件*//*

清理旧的配置信息*//*清理上次构建时生成的make

menuconfig/*

进入后直接Exit并且save*/make

dep

检查各模块的依赖关系make

bzImage

生成新内核(压缩格式),Linux内核的构建将新内核复制到/boot下:cparch/i386/boot/bzImage

/boot/bzImage-new注意:此处arch为相对路径,完整路径前面有/usr/src/linux修改LILO配置文件/etc/lilo.conf,在尾部添加(不能 修改原有内容)运行:/sbin/lilo6.重启虚拟机:reboot,系统启动时按tab选择新的内核image=

/boot/bzImage-newlabel=linux-newroot=/dev/sda5initrd=/boot/initrd.imgappend=“”read-only应与其他引导内核相同这里与其他内核相同应与其他引导内核相同应与其他引导内核相同系统调用实现如何使用系统调用

对于内核本身提供的系统调用,在libc中都已经提供了相应的API,可以当做普通的库函数调用:对于自定义的系统调用,需要经过特殊处理:#include

<linux/unistd.h>/*

generate

the

stub

for

our

new

syscall*

int sys_mycall(int

a,

int

b)*/_syscall2

(int,

mycall,int,

a, int,

b);//注意参数与mycall要求的参数一致课设题目的测试函数题目要求的测试函数可能形如#include

<linux/unistd.h>_syscall1(int,mycall,int,ret);//假设mycall接受一个int参数int

main(){printf("My

name

is

XXX,and

my

number

is%d.\n",mycall(0));printf("The

last

of

my

numberis%d.\n",mycall(1));return

0;}说明使用2.4或更高版本的内核可获得加分参考资料:google搜索“linux增加系统调用”实验内容3

Device

DriverLinux设备分类字符设备

(character

devices)以字符为单位进行I/O操作;只能顺序存取;无需缓冲区;如:Keyboard,Serial

Port块设备

(blockdevices

)以固定大小的数据块为单位进行I/O操作;数据块可以被随机存取;需要一定内存做为缓冲区;如:HD,CDROM,Floppy网络设备

(Network

devices)面向数据包的传输设备在文件系统中没有相应的inode表示,只有一个符号名,如eth0。Linux将硬件设备看做一类特殊的文件(/dev/*)Linux的设备管理是和文件系统紧密结合的,各种设备都以文件的形式存放在/dev目录下,称为设备文件。应用程序可以打开、关闭和读写这些设备文件,完成对设备的操作,就像操作普通的数据文件一样设备是应用访问设备的途径;设备文件代表一个驱动程序的入口及与之相关联的数据结构;为设备编写几个基本的函数,向虚拟文件系统(VFS)注册就可以完成设备驱动程序;安装设备驱动程序就是将该设备的操作函数集在内核中注册。/proc/devices列出系统当前的设备。设备文件设备文件的3个基本属性:类型:Block

orcharacter主设备号(Major

number)说明设备的类型(1

to

255)和各个设备驱动相关

次设备号(Minor

number)

区分使用同一驱动程序的不同设备。创建设备文件:

mknod

/dev/foo

b

60

0主设备号60,次设备号0

例:运行命令

’ls

-l /dev/hda*

/dev/tty[012]’查看设备的3个属性设备文件brw-rw----1rootcdrom3,0May61998hdabrw-rw----1rootdisk3,1May61998hda1brw-rw----1rootdisk3,2May61998hda2brw-rw----1rootdisk3,3May61998hda3crw-------1rootroot4,0

Dec

29

09:55

tty0crw-------1rootroot4,1Dec2909:56tty1crw-------1rootroot4,2Dec2909:56tty2Documentation/devices.txt设备驱动程序用户进程请求设备服务流程图用户进程V

F

S设备驱动程序设备控制器设备本身OS可管辖范围设备制造商范围1.

用户进程发出I/O请求,系统将请求处理下传到VFS上;VFS通过驱动程序提供的接口将任务分配到驱动程序;驱动程序根据需要对设备控制器进行操作;4.

设备控制器去控制设备。VFSLinux文件管理模块包括2

部分实现:文件系统无关部分定义文件管理的通用工作,如检查访问权限、确定磁盘块需要读写的时机等。文件系统相关部分定义与具体文件系统相关的工作,如确定磁盘块的定位、指引设备驱动读写特定的块等。内核通过一个抽象层隐藏了特定文件系统的实现

细节,并通过该抽象层管理多个不同的文件系统;该抽象层就是Virtual

File

System

(VFS)。物理文件系统层VFSMINIXEXT2MSDOS用户进程内核其他子系统内

间用户空间VFS设备驱动程序设备缓存VFS对下:管理各物理文件系统设备驱动程序组成设备服务子程序每个服务子程序只处理一种设备或与其紧密相关的设备;从与设备无关的软件中接受抽象的命令并执行;中断处理子程序主要用来检测I/O操作是否完成,以唤醒被阻塞的进程。特点驱动程序属于内核代码的一部分,运行在内核空间;向OS内核提供统一接口,便于内核对设备的管理;在编译内核时,连入内核的设备驱动程序是可以配置的;多以内核模块方式实现,使系统资源有效利用。设备驱动程序管理I/O操作结束的策略:内核发出I/O请求后等待(不可行的方式)轮询内核发出I/O请求后,驱动程序启动设备;内核周期性地查询设备状态,直到设备完成I/O操作;中断驱动程序启动设备,同时自行挂起(suspend),直到设备完成并引发一个中断请求(IRQ);IRQ一发生,相应的中断服务程序(ISR)将被运行;用户进程被唤醒(waked

up)。struct

f…

…ile

{…dentry

*f_dentry;//设备文件的inode,dentry为目nux文件系统中某个索引节点(inode)的链接。file_operation

*f_op; //设备文件的操作集合struct录项,是Listruct…

…}设备驱动程序-设备文件structfile_operations{…ssize_t(*read)(struct

file*,char

*,size_t,loff_t*);ssize_t(*write)(struct

file

*,

const

char

*,

size_t,loff_t*);int

(*open)(struct

inode

*,

struct

file

*);int

(*release)(struct

inode

*,

struct

file

*);unsigned

int

(*poll)

(struct

file

*,

structpoll_table_struct

*);…}设备驱动的注册和管理内核设备驱动程序表结构(fs/devices.c) struct device_struct

{const

char

*name; //

devicenamestruct

file_operations

*

fops;};字符设备驱动程序:设备表:全局数组chrdevs[255],主设备号是它的下标向系统注册设备驱动:register_chrdev()/unregister_chrdev()缺省操作集合:file_ops:

def_chr_fops,仅定义了一个open方法;inode_ops:chrdev_inode_operations,仅定义了其中的file_operation=&def_chr_fops.Kernel

Module一段被独立编译的函数和数据的集合;可以被动态加载/卸载,加载成功后,其中代码、数据称为内核中的一部分;模块技术通常用于实现设备驱动程序、文件系统、网络协议、系统调用等;模块优点:可根据需要,在不重新编译内核的情况下,将新代码插入到内核而成为内核的有机组成;使得内核在内存的映像较小;具有更大的灵活性和可扩充性;调试方便。Kernel

Module命令lsmod(和文件/proc/modules)可列出了当前已经加载的所有模块,每个入口对应一个模块:模块名占用内存大小使用模块列表iptable_nat22904ip_conntrack29696[iptable_nat]iptable_mangle2776ide-cd35772cdrom34176[ide-cd]mousedev5656input6208[keybdev

mousedevhid]structmodule{

//

内存中的每个module,位于include/linux/module.hunsigned

long

size_of_struct;

//

==sizeof(module)struct

module

*next;

//将所有module构成一个链表const

char

*name;unsigned

long

size;//module名//以页为单位的模块大小union{atomic_t

usecount;long

pad;}uc;

//使用记数usagecounterunsigned

long

flags;unsigned

nsyms;struct

module_symbol

*syms;//AUTOCLEAN

et

al//该模块的符号表中元素个数//该模块export的符号表//本模块指向其他模块的个数,定义模块时已知//被依赖模块的数组(链)//引用本模块的模块列表(链),动态可unsigned

ndeps;struct

module_ref*deps;struct

module_ref

*refs;变,便于查询//模块初始化函数指针//模块清理函数指针……

……int

(*init)(void);void

(*cleanup)(void);};模块初始原型:int主要完成模块清理原型:voi完成卸载块API其它函数化函数(必须定义)init_module(void);模块初始化、登记模块API函数(必须定义)d

clean_module(void);模块时的清理工作、取消登记的模,数据结构、变量等符号。模块中的内//容模块中的代码运行于核心态#include

<linux/kernel.h>#include

<linux/module.h>int init_module(

){printk("Hello,

world\n");return

0;}void

cleanup_module(

){printk("Goodbye

myworld\n");}在Linux里,除了直接修改系统核心的源代码,把设备驱动程序加进核心里以外,还可以把设备驱动程序作为可加载的模块,由系统管理员动态地加载和卸载。Linux的模块可以用C语言编写,用gcc编译成目标文件(不进行链接,作为*.o文件存在),为此需要在gcc命令行里加上-c的参数。在编译时,还应该在gcc的命令行里加上这样的参数:-D_KERNEL_-DMODULE。由于在不链接时,gcc只允许一个输入文件,因此一个模块的所有部分都必须在一个文件里实现。内核模块的管理内核模块的管理编译好的模块*.o放在/lib/modules/xxxx/misc下(xxxx表示核心版本,如在核心版本为2.2.14时应该为/lib/modules/2.2.14/misc)然后用depmod-a使此模块成为可加载模块。模块用

insmod命令可手工装入内核模块,用rmmod命令可以卸载指定的模块,并可以用lsmod命令可查看当前装入的内核模块状态以及需求加载模块的使用计数及标志信息。在成功的向系统注册了设备驱动程序后(调用

register_chrdev成功后),就可以用mknod命令来把设备映射为一个特别文件,其它程序使用这个设备的时候,只要对此特别文件进行操作就行了。37Module管理接口System

Call

for

module

loadingallocate

space

for

a

moduleInitialize

a

moduleQuery

information

of

a

modulecreate_module(),init_module(),query_module(),delete_module(),get_kernel_syms()System

utilities:/sbin/insmod (安装模块)/sbin/rmmod (卸载模块)/sbin/lsmod (显示已加载模块)/sbin内的命令必须要root权限,主要是用于系统管理(而非普通使用)的命令小例子[root#]insmod

./hello.o(此处可能出现linux内核版本不一致,可在启动时选用做系统调用实验时编译的内核)Hello,world[root#]

rmmod

helloGoodbye

my

world#include

<linux/kernel.h>#include

<linux/module.h>模块初始化函数int init_module()

{

printk("Hello,

world\n");

return

0;}void

cleanup_module()

{

printk("Goodbye

my

world\n");}模块清理函数编译、安装、卸载:[root#]

gcc

–c

–Wall

–D

KERNEL

-DMODULE

hello.c信息也被记录在/var/log/messages字符设备实验内容实现一个字符设备,支持以下功能:用户可以向设备写入字符串,并覆盖设备中原有的字符串;用户可以从设备读出写入的字符串;用户通过系统调用ioctl清除设备中写入的字符串;设备关闭前,只能被打开一次.O_RDONLY);sizeof(yourmsg));件结束close(h);,

O_WRONLY);表示写入的字节der //

process

2:

writer000]; char

yourmsg[1000];BUF_DEV, int

h

=

open(RWBUF_DEV//打开设备失败时h<0h

<

0

int

n

=write

(h,yourmsg,ourmsg,

sizeof(yourmsg)+1);//写设备失败时n<0,否则<0,n=0表示文数close(h);//

process

1: rea

//

process

3:

cleanerchar

yourmsg[1 char

yourmsg[1000];int

h

=

open(RW

int

h

=

open(RWBUF_DEV,

O_RDWR);//打开设备失败时h<0//打开设备失败时int

n=ioctl

(h,

RW_CLEAR,0);int

n=read

(h,

y

//写设备失败时n<0,否则表示成功//读设备失败时n

close(h);字符设备驱动的实现//

rwbuf.h, driver

for

virtual

char-device#define

RWBUF_NAME

“rwbuf”//设备文件/dev/rwbuf#defineRWBUF_DEV“/dev/rwbuf”//

devicepath#defineRWBUF_MAJOR60//主设备号#defineRWBUF_CLEAR0x909090//

IO

CtrlCommand//

rwbuf.c, driver

for

virtual

char-device#include

“rwbuf.h”#include

<linux/kernel.h>#include

<linux/module.h>#include

<linux/fs.h>//

for

kernelprogramming// for

kernel

module

struct.// struct

file_operations可以不单独写.h文件//rwbuf.c, driver

for

virtual

char-deviceint

init_module(){printk("Hello

world\n");if

(

register_chrdev(RWBUF_MAJOR,RWBUF_NAME,&rwbuf_fops)){printk("register

error\n");return

-1;}printk("register

ok\n");return

0;}字符设备驱动的实现//rwbuf.c, driver

for

virtual

char-devicestatic

struct

file_operationsrwbuf_fops

={open:

rwbuf_open,release:

rwbuf_close,read:write:ioctl:rwbuf_read,rwbuf_write,rwbuf_ioctl,};//rwbuf.c, driver

for

virtual

char-devicevoid

cleanup_module(){if(unregister_chrdev(RWBUF_MAJOR,RWBUF_NAME)

!=0

)printk("unreg

err\n");elseprintk("unreg

ok\n");printk("bye\n");}字符设备驱动的实现//rwbuf.c, driver

for

virtual

char-devicestatic

struct

file_operationsrwbuf_fops

={open:

rwbuf_open,release:

rwbuf_close,read:write:ioctl:rwbuf_read,rwbuf_write,rwbuf_ioctl,};字符设备驱动的实现//

rwbuf.c,

driver

for

virtual

char-devicestatic

int

inuse=0;//only

one

process

permited

at

the

same

time,类似于一个锁

static

int

rwbuf_open(struct

inode

*inode,struct

file

*

filep){if

(inuse

==

1)

return

-1;inuse

=

1;MOD_INC_USE_COUNT; //

increase

the

use

count

in

struct

modulereturn

0;}static

int

rwbuf_close(

struct

inode

*inode,

struct

file

*

filep

)

{inuse

=

0;MOD_DEC_USE_COUNT;return

0;}字符设备驱动的实现//

rwbuf.c,

driver

for

virtual

char-device#define

rwbuf_size

200static

char

rwbuf[rwbuf_size];//

MAX

size

of

buffer//

the

buffer

keeping

stringstaticint rwlen

=

0;

//

length

ofstringstatic

ssize_t

rwbuf_write

(

struct

file

*

filep,const

char

*buf,{size_t

count, loff_t

*

ppos

)//判断写入的长度是否有效copy_from_user(rwbuf,buf,count);//从用户空间复制到内核空间rwlen

=

count;//

print

some

message

byprintk();return

count;}字符设备驱动的实现//rwbuf.c, driver

for

virtual

char-devicestatic

ssize_t

rwbuf_read(

struct

file*

filep, char

*

buf,size_t

count, loff_t*

ppos){//判断读取的长度是否有效copy_to_user(buf,rwbuf,count);//从内核空间复制到用户空间//

print

some

message

byprintk()return

count;}字符设备驱动的实现//

rwbuf.c,

driver

for

virtual

char-devicestatic

int

rwbuf_ioctl

(

struct

inode*inode, struct

file

*filep,unsigned

int

cmd,unsigned

long

arg

){if

(

cmd

==

RW_CLEAR

)

{rwlen

=

0; //

clear

buf

by

set

its

len

to

zeroprintk("rwbuf

in

kernel

zero-ed\n");break;};return

0;}字符设备驱动的实现编译gcc

–crwbuf.c

–D

KERNEL

-DMODULE

–Wall安装与卸载:mknod

/dev/rwbuf

c600创建设备文件/sbin/insmod

rwbuf.o安装设备驱动/sbin/rmmod

rwbuf卸载设备驱动加分点使用2.4或2.6内核实验内容4SpinLock目标:从实践中理解OS内核的加锁机制和进程调度算法的设计,初步了解C语言和汇编语言的混合编程实验要求:使用spinLock、共享内存调用实现三个应用进程按指定顺序进行执行。(监控进程控制

3个进程的可执行位,依次放开3个进程)参考资料:/wiki/自旋锁什么是自旋锁?自旋锁是专为防止多处理器并发而引入的一种锁,它在内核中大量应用于中断处理等部分。自旋锁最多只能被一个内核任务持有如果一个内核任务试图请求一个已被持有的自旋锁,那么这个任务就会一直进行忙循环——旋转——等待锁重新可用。即每个进程不断试图获得锁,直到获得锁为止。要是锁未被持有,请求它的内核任务便能立刻得到它并且继续进行。自旋锁不允许任务睡眠(持有自旋锁的任务睡眠会造成自死锁,因为睡眠有可能造成持有锁的内核任务被重新调度,而再次申请自己已持有的锁),它能够在中断上下文中使用。什么是自旋锁?自旋锁的初衷:在短期间内进行轻量级的锁定。一个被持有的自旋锁使得请求它的线程在等待锁重新可用的期间进行自旋(浪费处理器时间),所以自旋锁不应该被持有时间过长。正是由于自旋锁使用者一般保持锁时间非常短,因此选择自旋而不是睡眠是非常必要的,自旋锁的效率远高于互斥锁。如果需要长时间锁定的话,最好使用信号量。当一个进程试图获得信号量而没有获得时,它会进入睡眠状态。单CPU时,自旋锁会让处理器动不了。因此,一般自旋锁实现会有一个参数限定最多持续尝试次数实验内容4SpinLock实现说明:i.环境:系统中共有a、b、c

3个应用进程、一个监控进程M和一个初始化进程Init,此外系统中还有一个共享内存SHM。SHM中有la,lb,lc

3个表示运行的锁变量,分属于A、B、C

3个应用进程,每个应用进程都通过spinlock试图获得锁,当获得锁后,它就开始正常运行。SHM中还有有3个分属于各个进程用来表示进程是否结束的互斥变量ta,tb,tc,以及3个分别用来控制访问ta、tb、tc的锁变量lta、ltb、ltc.实现算法说明:Init进程初始化共享内存,并设置la、lb、lc、ta、tb、tc、lta、ltb、ltc的初值。接着Init进程退出运行。进程i分别通过spinlock不停地试图获得li,一旦获得,进程就开始正常运行。在进程i退出前,通过spinlock试图获得lti,获得lti后,进程i将ti置为表示进程结束的值,然后退出。监控进程M按照指定的调度顺序逐步释放li。每释放一个li,M都要通过spinlock获得lti,然后通过访问ti,判断i进程是否结束,如果结束,那么M按照调度顺序释放下一个进程j对应的锁变量lj。SpinLock的编写算法思路1.保存现场(需要保存eax等spinlock函数用到的寄存器)2.利用原子指令cmpxchg(比较交换指令)尝试加锁3.利用cmpxchg指令执行完毕后的标志位,判断是否已加锁成功,若加锁不成功,那么则通过跳转指令继续第2步。4.恢复现场SpinLock的编写原子

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论