操作系统实验指导书Word文件下载.docx
- 文档编号:18366805
- 上传时间:2022-12-15
- 格式:DOCX
- 页数:46
- 大小:370.60KB
操作系统实验指导书Word文件下载.docx
《操作系统实验指导书Word文件下载.docx》由会员分享,可在线阅读,更多相关《操作系统实验指导书Word文件下载.docx(46页珍藏版)》请在冰豆网上搜索。
五、思考题及其它
Linux系统在用户登陆和操作界面以及文件系统上有哪些特点
进程管理
(验证)
1.理解进程概念以及进程和程序的区别;
2.理解进程并发执行的实质;
3.掌握解决进程同步问题的方法;
4.培养学生分析和设计程序的能力。
1.实验相关知识点
进程的基本概念;
进程控制;
进程同步;
经典进程的同步问题。
2.系统调用
系统调用是一种进入系统空间的办法。
通常,在OS的核心中都设置了一组用于实现各种系统功能的子程序,并将它们提供给程序员使用。
程序员在需要OS提供某种服务的时候,便可以调用一条系统调用命令,去实现希望的功能,这就是系统调用。
因此,系统调用就像一个黑箱子一样,对用户屏蔽了操作系统的具体动作而只是提供了调用功能的接口。
不同的操作系统有各自的系统调用方法。
如windowsAPI,便是windows的系统调用。
Linux的系统调用与之不同的是源于Linux内核代码完全公开,所以可以细致的分析出其系统调用的机制。
3.系统调用和普通函数的区别
(1)运行于不同的系统状态用户程序可以通过系统调用进入系统空间,在
核心态执行;
而普通函数则只能在用户空间当中运行。
(2)通过软中断切换由于用户程序使用系统调用后要进入系统空间,所以
需要调用一个软中断;
而普通函数在被调用时则没有这个过程。
4.系统调用的类型
系统调用的作用与它属在的操作系统有密切关系。
根据操作系统的性质不同,它们所提供的系统调用会有一定的差异,不过对于普通操作系统而言,应该具有下面几类系统调用:
进程控制类型
文件操纵类型
进程通信类型
信息维护类型
5.系统调用的实现机制
由于操作系统的不同,其系统调用的实现方式可能不一样,然而实现机制应该是大致相同的,一般包含下面几个步骤:
(1)设置系统调用号,在系统当中,往往设置多条系统调用命令,并赋予每条系统调用命令一个唯一的系统调用号。
根据分配系统调用号方式的不同分为:
直接方式和参数表方式。
(2)处理系统调用
操作系统中有一张系统调用入口表。
表中的每个表目都对应一条系统调用命令,它包含有该系统调用自带参数的数目、系统调用命令处理程序的入口地址等等。
操作系统内核便是根据所输入的系统调用号在该表中查找到相应的系统调用,进而转入它的入口地址去执行它。
6.Linux的系统调用机制
Linux的系统调用是通过中断机制实现的。
中断这个概念涉及到计算机系统结构方面的知识,显然它与微处理器等硬件有着密不可分的关系。
中断(Interrupt),是指计算机在执行期间,系统内发生任何非寻常的或非预期的急需处理事件,使得CPU不得不暂时中断当前正在执行的程序而转去执行相应的事件处理程序,待处理完毕后再返回原来被中断处继续执行的过程。
其发生一般而言是“异步”的,换句话说就是在无法预测的情况下发生的(比如系统掉电)。
所以计算机的软硬件对于中断的相应反应完全是被动的。
软中断,是对硬中断的一种模拟,发送软中断就是向接收进程的proc结构中的相应项发送一个特定意义的信号。
软中断必须等到接收进程执行时才能生效。
陷阱(Trap),即由软件产生的中断,指处理机和内存内部产生的中断,它包括程运算引起的各种错误,如地址非法、校验错误、页面失效等。
它由专门的指令,如X86中的“INTn”,在程序中有意的产生。
所以说陷阱是主动的,“同步”的。
异常(Exception),一般也是异步的,多半是由于不小心或无意造成的,比如在进行除法操作时除数为0,就会产生一次异常。
7.相关函数
fork()函数用于创建一个新进程(在进程);
调用格式:
intfork();
其中返回int值的取值意义如下:
正确返回:
等于0:
创建子进程,从子进程返回的ID值;
大于0:
从父进程返回子进程的进程的进程的ID值;
错误返回:
等于-1:
创建失败。
进程和父进程的调度执行子进程被创建后就进入就绪队列和父进程分别独立地等待调度。
子进程继承父进程的程序段代码,子进程被调度执行时,也会和父进程一样从fork()返回。
从共享程序段代码的角度来看,父进程和子进程所执行的程序代码是同一个,在内存中只有一个程序段副本;
但是从编程的角度来看,为了使子进程和父进程做不同的事,需要在程序中区分父进程和子进程的代码段。
这就需要借助于从fork()带回的值来标志当前进程身份。
从fork()返回后,都会执行语句:
pid=fork();
得到返回的值pid,有如下几种情况:
(1)若pid小于0,则表示fork()出错,相应语句为:
if(pid<
0){
printf(“forkerror\n”);
exit(0);
}
(2)若pid等于0,则表示当前进程是子进程,继续执行的后面的代码是子进程要做的事,相应语句可写成:
if(pid==0){
printf(“Thechildprocessisrunningnow!
\n”);
(3)若pid大于0,则表示当前进程是父进程,继续执行的后面的代码是父进
程要做的事,相应语句可写成:
if(pid>
printf(“Theparentprocessisrunningnow!
由于父进程和子进程分别独立地进入就绪队列等待调度,所以谁会先得到调度是不确定的,这与系统的调度策略和系统当前的资源状态有关。
因此谁先从fork()返回,继续执行后面的语句也是不确定的。
父进程和子进程的存放及资源共享:
当父进程创建子进程时,首先为子进程分配由task向量数组的指针指向的task_struct结构的内存、所需的堆栈、页表等,创建进程标识号(在系统的进程标识号组中是唯一的),并且将其父进程的进程标识号填入task_struct中的家族信息中。
然后将父进程的task_struct的相关内容拷贝到子进程的task_struct中,对一些数据成员进行初始化。
子进程从父进程处继承的资源包括:
真实用户标识号和组标识号、有效用户标识号和组标识号、进程组标识号、对话标识号、控制终端、根目录与当前工作目录、设置用户标识号和设置组标识号记位、信号标识、文件描述符、文件缺省创建权限掩码、可访问的内存区、线程、环境变量及其它资源分配。
wait()函数
wait()函数常用来控制父进程与子进程的同步。
在父进程中调用wait()函数,则父进程被阻塞,进入等待队列,等待子进程结束。
当子进程结束时,会产生一个终止状态字,系统会向父进程发出SIGCHLD信号。
当接到信号后,父进程提取子进程的终止状态字,从wait()函数返回继续执行原程序。
其调用格式为:
#include<
sys/type.h>
sys/wait.h>
(pid_t)wait(int*status);
大于0:
子进程的进程ID值;
等于0:
其它。
等于-1:
调用失败。
kill()函数
系统调用格式
intkill(pid,sig)
参数定义
intpid,sig;
其中,pid是一个或一组进程的标识符,参数sig是要发送的软中断信号。
(1)pid>
0时,核心将信号发送给进程pid。
(2)pid=0时,核心将信号发送给与发送进程同组的所有进程。
(3)pid=-时,核心将信号发送给所有用户标识符真正等于发送进程的有效用户标识号的进程。
signal()
预置对信号的处理方式,允许调用进程控制软中断信号。
signal(sig,function)
头文件为:
signal.h>
intsig;
void(*func)()
signal()会依参数sig指定的信号编号来设置该信号的处理函数。
当指定信号到达时就会跳转到参数function指定的函数执行。
其中sig用于指定信号的类型,sig为0则表示没有收到任何信号,如下表:
值
名字
说明
01
SIGHUP
挂起(hangup)当终端机察觉到终止连线操作时便会传送这个信号
02
SIGINT
中断,当用户从键盘按^c键或^break键时,则会产生此信号
03
SIGQUIT
退出,当用户从键盘按quit键时,如CTRL+\,则会产生此信号
04
SIGILL
非法指令(进程执行了一个不合法德CPU指令)
05
SIGTRAP
跟踪陷阱(tracetrap),当子进程因被追踪而暂停时产生此信号给父进程
06
SIGIOT
IOT指令
07
SIGBUS
总线错误,BUS发生错误时会产生此信号
08
SIGFPE
浮点运算溢出
09
SIGKILL
杀死、终止进程的信号,此信号不能被拦截或忽略
10
SIGUSR1
用户自定义信号1
11
SIGSEGV
段违例(segmentationviolation),进程试图去访问其虚地址空间以外的位置
12
SIGUSR2
用户自定义信号2
13
SIGPIPE
错误的管道;
欲写入无读取端的管道时产生此信号
14
SIGALRM
闹钟。
当某进程希望在某时间后接收信号时发此信号
15
SIGTERM
软件终止(softwaretermination)
16
SIGSTKFLT
堆栈错误
17
SIGHLD
子进程暂停或结束时便会产生此信号给父进程
18
SIGCONT
此信号会让暂停的进程继续执行
19
SIGSTOP
此信号用来让进程暂停执行,此信号不能被拦截或忽略
20
SIGTSTP
当由键盘(CTRL+Z)表示暂停时就产生此信号
function:
该进程中的一个函数地址,在核心返回用户态时,它以软中断信号的序号作为参数调用该函数。
function的解释如下:
(1)function=1(SIG_IGN)时,进程对sig类信号不予理睬,亦即屏蔽了该类信号;
(2)function=0(SIG_DFL)时,缺省值,进程在收到sig信号后应终止自己;
(3)function为非0,非1类整数时,function的值即作为信号处理程序的指针。
pause()函数
让进程暂停(进入睡眠状态)直到被信号(signal)所中断。
waitpid()函数
会暂停目前进程的执行,直到有信号来到或子进程结束。
exit()函数
终止进程的执行。
系统调用格式:
voidexit(status)
intstatus;
其中,status是返回给父进程的一个整数,以备查考。
为了及时回收进程所占用的资源并减少父进程的干预,UNIX/LINUX利用exit()来实现进程的自我终止,通常父进程在创建子进程时,应在进程的末尾安排一条exit(),使子进程自我终止。
exit(0)表示进程正常终止,exit
(1)表示进程运行有错,异常终止。
如果调用进程在执行exit()时,其父进程正在等待它的终止,则父进程可立即得到其返回的整数。
核心须为exit()完成以下操作:
(1)关闭软中断
(2)回收资源
(3)写记帐信息
(4)置进程为“僵死状态”
四、实验步骤
1.进程的创建
编写一段程序,使用系统调用fork()创建两个子进程。
当此程序运行时,在系统中有一个父进程和两个子进程活动。
让每一个进程在屏幕上显示一个字符:
父进程显示字符“a”;
子进程分别显示字符“b”和字符“c”。
试观察记录屏幕上的显示结果,并分析原因。
stdio.h>
main()
{
intp1,p2;
while((p1=fork())==-1);
/*创建子进程p1*/
if(p1==0)/*子进程创建成功*/
putchar('
b'
);
else
while((p2=fork())==-1);
/*创建另-个子进程*/
if(p2==0)/*子进程创建成功*/
c'
else
a'
/*父进程执行*/
}
2.软中断通信
(1)编写一段程序,使用系统调用fork()创建两个子进程,再用系统调用signal()让父进程捕捉键盘上来的中断信号(即按ctrl+c键),当捕捉到中断信号后,父进程用系统调用kill()向两个子进程发出信号,子进程捕捉到父进程发来的信号后,分别输出下列信息后终止:
Childprocess1iskilledbyparent!
Childprocess2iskilledbyparent!
父进程等待两个子进程终止后,输出以下信息后终止:
Parentprocessiskilled!
<
参考程序>
#include<
unistd.h>
intwait_mark;
voidwaiting(),stop();
voidmain()
{intp1,p2;
signal(SIGINT,stop);
while((p1=fork())==-1);
if(p1>
0)/*在父进程中*/
{
while((p2=fork())==-1);
if(p2>
0)/*在父进程中*/
{
wait_mark=1;
waiting();
kill(p1,16);
kill(p2,17);
wait();
printf("
parentprocessiskilled!
\n"
exit(0);
else/*在子进程2中*/
signal(17,stop);
childprocess2iskilledbyparent!
}
else/*在子进程1中*/
signal(16,stop);
childprocess1iskilledbyparent!
exit(0);
voidwaiting()
{
while(wait_mark!
=0);
voidstop()
wait_mark=0;
实验要求:
运行程序并分析结果。
如果把signal(SIGINT,stop)放在
号和
号位置,结果会怎样并分析原因。
该程序段前面部分用了两个wait(0),为什么?
该程序段中每个进程退出时都用了语句exit(0),为什么?
(2)修改上面的程序,增加语句signal(SIGINT,SIG_IGN)和语句signal(SIGQUIT,SIG_IGN),再观察程序执行时屏幕上出现的现象,并分析其原因。
intpid1,pid2;
intEndFlag=0;
pf1=0;
pf2=0;
voidIntDelete()
kill(pid1,10);
kill(pid2,12);
EndFlag=1;
voidInt1()
childprocess1iskilledbyparent!
voidInt2()
childprocess2iskilledbyparent!
main()
intexitcode;
signal(SIGINT,SIG_IGN);
signal(SIGQUIT,SIG_IGN);
while((pid1=fork())==-1);
if(pid1==0)
{
signal(SIGUSR1,Int1);
signal(SIGINT,SIG_IGN);
pause();
exit(0);
else
while((pid2=fork())==-1)
if(pid2==0)
signal(SIGUSR2,Int2);
pause();
signal(SIGINT,IntDelete);
waitpid(-1,&
exitcode,0);
/*等待任何子进程中断或结束*/
parentprocessiskilled\n"
(3)生产者和消费者问题
编制一段程序,实现经典的进程同步问题生产者和消费者问题。
使用Linux系统提供的信号量机制实现。
两个子进程作为消费者,一个子进程作为生产者。
sys/mman.h>
sys/types.h>
linux/sem.h>
fcntl.h>
errno.h>
time.h>
#defineMAXSEM5
//声明三个信号灯ID
intfullid;
intemptyid;
intmutxid;
intmain()
structsembufP,V;
;
unionsemunarg;
//声明共享内存
int*array;
int*sum;
int*set;
int*get;
//映射共享内存
array=(int*)mmap(NULL,sizeof(int)*5,PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);
sum=(int*)mmap(NULL,sizeof(int),PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);
get=(int*)mmap(NULL,sizeof(int),PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);
set=(int*)mmap(NULL,sizeof(int),PROT_READ|PROT_WRITE,MAP_SHARED|MAP_ANONYMOUS,-1,0);
*sum=0;
*get=0;
*set=0;
//生成信号灯
fullid=semget(IPC_PRIVATE,1,IPC_CREAT|00666);
emptyid=semget(IPC_PRIVATE,1,IPC_CREAT|00666);
mutxid=semget(IPC_PRIVATE,1,IPC_CREAT|00666);
//为信号灯赋值
arg.val=0;
if(semctl(fullid,0,SETVAL,arg)==-1)perror("
semctlsetvalerror"
arg.val=MAXSEM;
if(semctl(emptyid,0,SETVAL,arg)==-1)perror("
arg.val=1;
if(semctl(mutxid,0,SETVAL,arg)==-1)perror("
setctlsetvalerror"
//初始化P,V操作
V.sem_num=0;
V.sem_op=1;
V.sem_flg=SEM_UNDO;
P.sem_num=0;
P.sem_op=-1;
P.sem_flg=SEM_UNDO;
//生产者进程
if(fork()==0){
inti=0;
while(i<
100)
semop(emptyid,&
P,1);
semop(mutxid,&
P,1);
array[*(set)%MAXSEM]=i+1;
Producer%d\n"
array[(*set)%MAXSEM]);
(*set)++;
V,1);
semop(fullid,&
i++;
sleep(10);
Producerisover"
}else{
/
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 操作系统 实验 指导书