《操作系统》2007实验讲义(2)

来源:互联网 发布:虚假记忆知乎 编辑:程序博客网 时间:2024/04/20 10:32

实验一  用户接口实验

一.实验目的

1. 了解Linux操作系统的启动与登录方法

2. 掌握常用Red Hat Linux命令的使用方法,掌握图形用户界面下的基本操作

3. 了解Linux命令中参数选项的用法和作用

4. 熟悉操作系统的命令接口、图形接口和程序接口的区别与联系

5了解命令行和集成环境下C程序的编写及运行方法

二.实验工具与设备

已安装Linux操作系统的计算机并通过网络与Linux服务器连接。

三.实验内容

1.熟悉开机后登录进入Linux系统和退出系统的过程

以适当的用户名在装有Linux系统的计算机中登录Linux系统。

1)登录

  开启计算机电源,计算机进行启动过程,如果安装了X Windows,系统启动时自动启动X WindowsX Windows的登录在图形界面下进行。图1-1所示是GNOME的登录界面,在菜单中选择合适的语言(Language)和会话(Session),分别输入用户账号(如root)和用户密码后,系统进入图形用户界面。

 

 

 

 

 

 

 


1-1  GNOME的登录界面

若没有安装X Windows,或由于显示卡等原因不能启动X Windows时,可以在字符界面(提示符状态)登录Linux。当系统启动到出现提示Login:时,输入用户账号,按回车键,出现Password时,输入用户密码,按回车键,即以自己的用户名登录到Linux系统中了。

从字符界面登录Linux系统后,还可以用以下命令启动X Windows

       [root@localhost /root]# startx

       在图形用户界面下,也可以不退出X Windows直接进入提示符状态使用Linux命令:在主菜单中选择系统工具→终端选项,弹出如图1-2所示窗口,在该窗口中可以使用字符命令。

2)开始操作

       在桌面双击从这里开始图标,打开“Nautilus”Start Here)窗口,如图1-3所示。该窗口有应用程序系统设置首选项”3个图标,包含了Redhat Linux 9.0中最常用的功能。

 

 

 

 

 

 

 

 


1-2  GNOME的终端仿真程序(Terminal Emulation Program

 

 

 

 

 

 

 

 

 

 

 


1-3  GNOME中的“Start Here”窗口

3)创建用户账号

       一般情况下不应直接用root账号进行操作。在Linux中,可以为每一位用户创建一个用户账号,使用时以个人账号登录。

       Red Hat Linux 9.0中创建用户账号的方法有以下两种:

a. 在图形界面中创建用户账号

需要超级用户创建用户,若系统只有root用户,则以root账号登录;若有其他用户账号,也可以以其他用户账号登录,但在创建用户账号需要输入root账号密码。

在桌面双击从这里开始图标,打开系统设置窗口;在该窗口中双击用户和群组图标,弹出用户管理窗口。若不是以root账号登录,此时要求用户输入root账号密码。

用户管理窗口的工具栏中单击添加用户按钮,弹出创建用户对话框。在该对话框中填入用户名、用户全称和密码,并选取创建主目录名和为用户创建新的组复选框,用户登录shell则可以选取默认值。

单击确定按钮,在用户列表中添一个用户账号。若需要再创建其他用户账号,可继续单击添加用户按钮进行创建。

在工具栏中单击添加群组按钮,打开创建用户组的对话框,在该对话框中填入用户组名称后,单击确定按钮,可创建一个用户组。

b. 在字符状态下创建用户账号

       在字符(命令行)状态下用adduser(或useradd)命令创建用户账号,具体方法见相关教材,这里不赘述。

4)退出系统

       Linux中不能直接关闭计算机电源,或直接按主机面板的Reset键重新启动计算机。在图形界面下,可以在主菜单中选择注销关机选项退出系统。在字符界面下,可以用shutdown 命令退出系统。退出系统后,才能关闭计算机电源或重新启动计算机。在字符界面下,用shutdown命令退出或重新启动系统。

例如:shutdown –r now    表示马上关闭并重新启动

       shutdown –h +10    表示10分钟后关闭并终止

2.使用Linux常用命令以及图形化接口X-Window

常用的UNIX/Linux命令

1Linux命令的执行

可以在Linux命令提示符下,直接输入Linux命令,然后按回车键。如果命令不在缺省路径,需要输入命令和完整的路径。

注意: Linux命令区分大小写字母。例如:ls/usr/bin/install

缺省路径:默认的查找执行文件的路径。每个用户登录时都有缺省路径,若输入命令不指定路径,则在缺省路径中的所有路径中按顺序检查与命令相关联的文件。

可以用以下命令查找缺省路径:

echo $PATH

输出结果的格式:/usr/local/bin:/bin:/usr/bin:/home/mj/bin:/usr/X11R6/bin,其中,冒号用来分隔不同目录。

2)文件操作命令

文件操作命令主要包括查看文件命令(ls)、显示文件内容命令(cat)、文件复制命令(cp)、文件改名命令(mv)、删除文件命令(rm)。

3)目录操作命令

       目录操作命令主要包括改变当前目录命令(cd)、显示当前目录命令(pwd)、建立子目录(mkdir)和删除子目录(rmdir)。

4)用户和系统管理操作命令

用户和系统管理操作命令主要包括登录和注销命令(loginlogout)、添加和更改用户命令、修改用户密码命令(passwd)和关机命令(shutdown)。

5)其他操作命令

其他操作命令主要包括链接命令(ln),清屏命令(clear),显示日期、时间和月历命令,获取注册信息命令和查看命令帮助信息命令(man)等。

       Linux的命令很多,用法也很灵活,熟练掌握这些命令不可能通过一两个实验就能完成,需要大量反复的练习。

3.命令行和集成环境下C程序的编写及运行

1)集成环境

开始(大脚丫)—> KDE menus > Development > KDevelop 2.1 > 选主菜单Project > New > Terminal (C, C++) > Next…Create…Exit > Build菜单中的Compile > 通过后Make > Execute即可运行。

2gcc行编译

首先需要在编辑器中编写好源文件,如采用:开始 > programs > Applications > gedit,或者其他的编辑器。gccGUN C Compile的缩写。

gcc运行格式:              gcc  [options]  [filename]

    选项options可为:

-x language:  指定使用的语言(C,C++或汇编);

-c 只对文件进行编译和汇编,但不进行连接, 如:gcc –c test.c

-S:只对文件进行汇编,但不进行编译和连接,如:gcc –S test.c

-E 只对文件进行预处理;

-o [file1] file2: 将文件file2编译成可执行文件file1;

-l library: 用来指定所使用的库文件;

-I directory: include文件的搜索指定目录;

-w: 禁止警告信息;

-pedantic 严格要求符合ANSI标准;

-Wall:  显示附加的警告信息;

-v 显示gcc版本

    使用步骤:

方法一:

gcc  test.c8   //编译test.c程序

./a8          //执行a.out文件,该文件中包含了test.c程序

方法二:

              gcc  –o  test  test.c8   //test.c编译为test.out

              ./test8                 //执行test.out

4.用C语言编写一小程序,使其可以通过某个系统调用来获得OS提供的某种服务

GNOME图形界面下,主菜单(相当于Windows中的开始菜单)的各个子菜单中包括了已安装的应用程序快捷方式,直接单击这些菜单项即可运行相应的应用程序。1-4  运行应用程序对话框。GNOME图形界面下,还可以用主菜单中的运行程序命令打开运行程序对话框(见图1-8),系统中已安装的程序都在已知的应用程序列表中列出。在列表中选择要运行的应用程序并单击确定按钮,即可运行该应用程序。其中,可以选择是否在终端仿真程序中运行。

                    

 

 

 

 

 

 

 

 

 

1-4  运行应用程序对话框

四.思考题

1.       OS向用户提供的命令接口、图形接口和程序接口分别适用于哪些场合?

2.       Linux的登录、退出过程和Windows有何区别与联系?

3.       怎样编写、运行C语言程序?


实验二  进程的控制

一.实验目的

1.熟悉和理解进程和进程树的概念,掌握有关进程的管理机制

2.通过进程的创建、撤销和运行加深对进程并发执行的理解

3.明确进程与程序、并行与串行执行的区别

4.掌握用C程序实现进程控制的方法

二.实验工具与设备

已安装Linux操作系统的计算机。

三.实验预备内容

1.阅读Linuxsched.h源代码文件,加深对进程管理概念的理解

2.阅读Linuxfork.c源代码文件,分析进程的创建过程

四.实验内容

1.了解系统调用fork( ), exec系列函数,exit( ), waitpid( )的功能和实现过程

1)进程的创建

a. 派生进程

#include<unistd.h>

pid_t fork(void);

pid_t vfork(void);

调用fork时,系统将创建一个与当前进程相同的新进程,其与原有进程具有相同的数据、连接关系和在程序同一处执行的连续性。原进程称为父进程,新生进程称为子进程。子进程是父进程的一个拷贝,子进程获得同父进程相同的数据,但是同父进程使用不同的数据段和堆栈段。

fork调用将执行两次返回,即分别从父进程和子进程分别返回,从父进程返回时的返回值为子进程的PID,而从子进程返回时的返回值为0,并且返回都将执行fork之后的语句。调用出错时返回值为-1,并将errno置为相应值。调用vfork的作用与fork基本相同,但vfork并不完全拷贝父进程的数据段,而是和父进程共享数据段。

2.1


#include<sys/types.h>

#include<stdio.h>

#include<unistd.h>

int main()

{

 pid_t pid;   char *message;   int n;

 printf(“Fork program starting/n”);

pid=fork()

switch(pid){

case –1:

printf(“Fork error!/n”); exit(1);

   case 0:

message= “Child process is printing.”;

   n=5;

   for(;n>0;n--)

{ puts(message);  sleep(1);}

break;

 default:

  message= “Parent process id printing.”;

  n=3;

for(;n>0;n--)

{ puts(message); sleep(1);}

break;

}

exit(0);

}


b. 创建执行其他程序的进程

可以使用exec族的函数执行新的程序,以新的子进程来完全替代原有的进程。

#include<unistd.h>

int execl(const char *pathname, const char *arg, …, (char *)0);

int execlp(const char *filename, const char *arg, …, (char *)0);

int execle(const char *pathname, const char *arg, …, (char *)0, const char *envp[ ]);

int execv(const char *pathname, char *const argv[ ]);

int execvp(const char *filename, char *const argv[ ]);

int execve(const char *pathname, char *const argv[ ], char *const envp[ ]);

函数名中含有字母“l”的函数,其参数个数不定。其参数由所调用程序的命令行参数列表组成,最后一个NULL表示结束。函数名中含有字母“v”的函数,则是使用一个字符串数组指针argv指向参数列表,这一字符串数组和含有”l”的函数中的参数列表完全相同,也同样以NULL结束。函数名中含有字母”p”的函数可以自动在环境变量PATH指定的路径中搜索要执行的程序。函数名中含有字母”e”的函数,比其他函数多含有一个参数envp。该参数是字符串数组指针,用于制定环境变量。

 2.2:派生一个子进程后,在子进程中使用execl函数调用shell命令sh


#include<sy/types.h>

#include<stdio.h>

#include<unistd.h>

int main()

{

 pid_t pid;

 if(pid=vfork())<0) {printf(“Fork error!/n”); exit(1);}

 else if(pid==0)

 {

printf(“Child process PID:%d./n”,getpid());

setenv(“PS1”,”CHILD//$”,1);

  printf(“Process %4d: calling exec./n”,getpid());      if(execl(“/bin/sh”,”bin/sh”,”arg2”,NULL)<0)      {

   printf(“Process %4d:execle error!/n”,getpid());

   exit(0);

  }

printf(“Process %4d:You should never see this because the child is already gone./n”,  gepid());

printf(“Process %4d:The child process is exiting./n”, getpid());

}

else

{

 printf(“Parent process PID:%4d./n”,getpid());

 printf(“Process %4d:The parent has fork process %d./n”, pid);

 printf(“Process %4d:The Child had called exec or has exited./n”, getpid());

}

return 0;

}


2)进程等待

当一个进程结束时,Linux系统将产生一个SIGCHLD信号通知其父进程。在父进程未查询子进程结束的原因时,该子进程虽然停止了,但并未完全结束。此时这一子进程被称为僵尸进程(zombie process)。例如,在有些情况下父进程先于子进程退出,于是会看到在系统提示符“$”后子进程仍然在连续输出信息,这对用户是非常不友好的。我们可以使用系统调用wait,来让父进程处于等待状态,直到子进程退出后才继续执行下面的语句。

#include<sys/types.h>

#include<sys/wait.h>

pid_t wait(int *stat_loc);

参数stat_loc是一个整型指针,当子进程结束时,将子进程的结束状态字存放在该指针指向的缓冲区。当调用wait时,父进程将被挂起,直至该进程的某个子进程结束时,该调用返回。如果没有子进程,则错误返回。调用成功,返回值为子进程的进程号;调用失败时,返回值为-1

2.3


#include<sys/types.h>

#include<stdio.h>

#include<sys/wait.h>

#include<unistd.h>

int main()

{

 pid_t pid;   char *message;   int n,exit_code;

 printf(“Fork program starting/n”);

pid=fork()

switch(pid){

case –1:

printf(“Fork error!/n”); exit(1);

   case 0:

message= “Child process is printing.”;

    n=5;  exit_code=37;

  break;

  default:

message=“Parent process id printing.”;

n=3; exit_code=0;

break;

}

for(;n>0;n--)

{ puts(message); sleep(1);}

if(pid)

{

 int stat_val;

 pid_t child_pid;

 child_pid=wait(&stat_val);

printf(“Child has finished: PID=%d./n”,child_pid);

if(WIFEXITED(stat_val))

printf(“Child exited with code %d./n”, WEXITSTATUS(stat_val));

else

printf(“Child terminated abnormally./n”);

}

exit(0);

}


3)进程的终止

进程结束可通过相应的函数实现:

#include<stdlib.h>

void exit(int status);            //终止正在运行的程序,关闭所有被该文件打开的文件描述符

int atexit(void (*function)(void)); //用于注册一个不带参数也没有返回值的函数以供程序正常退出时被调用。参数function是指向所调用程序的文件指针。调用成功返回0,否则返回-1,并将errno设置为相应值

int on_exit(void (*function)(int,void *),void *arg); //作用与atexit类似,不同是其注册的函数具有参数,退出状态和参数arg都是传递给该函数使用

void abort(void);              //用来发送一个SIGABRT信号,该信号将使当前进程终止

2.4


#include<sys/types.h>

#include<sys/wait.h>

#include<stdio.h>

void h_exit(int status);

static void forerror(void);

static void waiterror(void);

int main(void)

{

 pid_t pid;   int status;

 if(pid=fork())<0)  atexit(forkerror);

 else if(pid==0) abort();

 if(wait(&status)!=pid) atexit(waiterror);

 h_exit(status);

}

 

void h_exit(int status)

{

if(WIFEXITED(status))

printf(“Normal termination, exit status=%d./n”, WEXITSTATUS(status));

else if(WIFSIGNALED(status))

printf(“Abnormal termination, exit status=%d./n”, WEXITSTATUS(status));

}

 

void forkerror(void)

{

 printf(“Fork error!/n”);

}

 

void waiterror(void)

{

 printf(“Wait error!/n”);

}


4system函数

用户可以使用该函数来在自己的程序中调用系统提供的各种命令。

#include<stdlib.h>

int system(const char *cmdstring);

参数cmdstring 是一个字符串指针,指向表示命令行的字符串。该函数的实现是通过调用forkexecwaitpid函数来完成的,其中任意一个调用失败则system函数的调用失败,故返回值较复杂。

2.5


#include<stdlib.h>

#include<stdio.h>

int main()

{

printf(“running ps with system./n”);

 system(“ps –ax”);

printf(“Done./n”);

exit(0);

}


5)进程组

一个进程除了进程ID外,还有一个进程组ID。进程组是一个或多个进程的集合,同一个进程组中进程都有一个统一的进程组ID

#include<sys/types.h>

#include<unistd.h>

pid_t getpgrp(void); //用于返回调用它的进程的进程组号

    int setpgid(pid_t pid,pid_t pgid);  //创建一个新的进程组或将一个进程加入一个已存在的进程组。当参数pidpgid相等时,用于创建一个新的进程组;当pgid是一个已存在的进行组ID时,将pid代表的进程加入该进程组。返回值:0(成功),-1(失败)

6)时间片的分配

a. 设置和获取进程的调度策略和参数

#include<sched.h>

int shced_setscheduler(pid_t pid, int policy, const struct sched_param *param);  //设置,0(成功),否则-1

int sched_getscheduler(pid_t pid);                            //获取,非负数(成功),-1(失败)

参数param用于保存进程的调度参数

policy表示所设置的调度策略:SCHED_OTHER:缺省的调度策略,按通常方法分配时间片;

SCHED_FIFO:对应于先进先出的规则,实时分配时间片,可以抢占使用SCHED_OTHER的进程

SCHED_RR  :轮换规则,实时分配时间片

 b. 优先级设定

优先级的值越小,优先权越高

#include<unistd.h>

int nice(int inc);        //改变进程的动态优先级,inc为所设的值,返回值:0(成功),-1(失败)

#include<sys/time.h>

#include<sys/resource.h>

int setpriority(int which, int who, int prio);         //设置进程、进程组或用户的动态优先级

int getpriority(int which, int who);    //获取进程、进程组或用户的动态优先级,which用于指定所操作的对象:PRIO_PROECSS(进程), PRIO_PGRP(进程组), PRIO_USER(用户)who用于指定函数所设置的进程;prio用于指定进程优先级(-20~20

2.程序设计

1)编写一段程序,使用系统调用fork( )创建两个子进程。当此程序运行时,在系统中有一个父进程和两个子进程活动。让每一个进程在屏幕上显示一个字符:父进程显示字符’a’;子进程分别显示字符’b’’c’。观察屏幕上的显示结果,并分析原因。

2编写一段程序,使用系统调用fork( )创建一个子进程。子进程通过系统调用exec系列函数调用命令ls,调用exit( )结束。而父进程则调用waitpid( )等待子进程结束,并在子进程结束后显示子进程的标识符,然后正常结束。

五.思考题

1.怎样用C程序实现进程的控制? 当首次调用新创建进程时,其入口在哪里?

2.系统调用fork( )是如何创建进程的?系统调用exit( )是如何终止一个进程的?

3.系统调用exec系列函数是如何更换进程的可执行代码的?

 

 

原创粉丝点击