操作系统第二次实验
实验4 shell初步
一、实验目的
- 熟悉shell的基本操作
- 使用LINUX中最常用的数据处理命令
二、实验要求
- 使用通配符来进行文件名的查找
- 重定向标准输入、标准输出、标准错误输出
- 使用管道操作,将一个进程的输出作为另一个进程的输入
- 使用群命令和命令行续行符
- 在指定的目录中,对满足条件的文件名进行递归查找
- 了解vi编辑器的基本用法
- 在文本文件中,查找符合指定模式的文本行
- 将文本文件中的各行,按顺序排列
- 显示一个文本文件的前几行或者后几行
三、实验预备知识
1. shell简介
shell是一个命令解释程序, 它提供了操作系统与用户之间的主要界面,控制用户与内核之间的交互作用。
除了解释用户从键盘上键入的命令外,shell也可以解释存储在文件中的命令。在Linux中,存储命令的文本文件称为shell脚本,可以具有任意的扩展名。shell实际上是一种编程语言,shell编程广泛地用于获取经常执行的命令和过程。
2. shell特殊变量
$?——上一条命令的执行情况
$#——命令行参数的个数
$*——所有命令行参数的内容
$@——所有命令行参数的内容
$n——命令行的第n个参数
$$——当前shell程序的pid
$!——最近一个在后台运行的命令的pid
$-——当前选项标志
$_——上一条命令的最后一个参数
3. shell脚本
- 建立shell脚本
可以用来建立文本文件的方法都可以用来建立shell脚本。例如,可以用vi编辑器或是emacs编辑器来建立一个shell脚本,也可以通过重定向标准输入和输出在命令行上直接建立shell脚本文件。
如下使用cat命令建立shell脚本:
cat > print_user
echo User name:$LOGNAME
echo Home directory: $HOME
echo Current shell PID: $$
<Ctrl+D> - 执行shell脚本
(1)用子shell执行shell脚本文件要用子shell执行的脚本文件print_user,可以键入如下命令:
$sh print_user
(2)用“.”命令执行shell脚本文件
“.”命令的一般形式为:
$ . shell脚本文件名
(3)用exec命令执行shell脚本文件
该命令的一般形式为:
$ exec ./shell脚本文件名
四、实验内容
1. 通配符
- 通配符用于模式匹配,如文件名匹配、路经名搜索、字符串查找等。常用的通配符有*、?和括在方括号[ ]中的字符序列。用户可以在作为命令参数的文件名中包
- 含这些通配符,构成一个所谓的“模式串”,在执行过程中进行模式匹配。
- *代表任何字符串(长度可以不等,可以为空字符串)。
例如:“a*”匹配以a打头的任意字符串。 - ?代表任何单个字符。
- [ ]代表指定的一个字符范围,只要文件名中[ ]位置处的字符在[ ]中指定的范围之内,那么这个文件名就与这个模式串匹配。
2. 重定向
重定向就是将标准输入、标准输出甚至标准报错重定向到一个文件。也就是说,可以让命令从某个文件中读取参数和数据,命令的输出结果也可以送至某个文件中而不是在终端上显示出来。实现重定向的最简单的方法是使用改向操作符。改向操作符可以将标准输入、标准输出和标准报错改向到某个文件。
例:将显示结果重定向到file文件当中。
$ls –l > file
另一 种实现重定向的方法是使用管道(|),它将一条命令的输出发送到另一条命令的输入。
例:将文件junk作为信件的内容,给自己发个mail
$cat junk|mail 用户名
3. 管道及tee
管道操作符将一条命令的输出定向到另一条命令的输入,而不是定向到终端或文件。
例:使用一个管道操作来计算当前目录中的文件数目
$ls | wc -w
tee命令作用可以用字母T来形象地表示。它把输出的一个副本输送到标准输出,另一个副本拷贝到相应的文件中。
例:将上述命令的结果同时输出到文件junk当中
$ls | wc –w | tee junk
4. 群命令及续行符
如果希望在成功地执行一个命令之后再执行另一个命令,或者在一个命令失败后再执行另一个命令,&&和||可以完成这样的功能。相应的命令可以是系统命令或shell脚本。
shell还提供了在当前shell或子shell中执行一组命令的方法,即使用()和{ }。
例:调用如下命令:显示日期;显示已经登录到系统中的所有用户名;显示当前目录的名称;显示当前目录中的所有文件名。
$ date; who; pwd; ls -a
这组命令将按顺序执行,不管前一命令是否出错,后一命令继续执行
例:将file1中的内容拷贝到file2当中,如果拷贝成功 ,则回应一个消息。
$cp file1 file2 && echo “cp is successful!”
例:使用||,实现若拷贝操作不成功,则返回一个消息。
$cp file3 file1 || echo “cp is failed!”
例:在上面的例子当中,若拷贝失败,则回应一个消息,并给自己发送一封邮件,最后退出。
$cp file3 file1 || (echo“cp is failed!”;mail chen ; exit)
5. find命令
find是一个非常有效的工具,它可以遍历当前目录甚至于整个文件系统来查找某些文件或目录。
find命令的一般形式为:find pathname -options [-print – exec –ok]
- pathname:find命令所查找的目录路径。
- -print:find命令将匹配的文件输出到标准输出。
- -exec:find命令对匹配的文件执行该参数所给出的shell命令。
- -ok 和- exec的作用相同,只不过在执行每一个命
令之前,都会给出提示,让用户来确定是否执行
例:在你的用户主目录中查找所有以字母s开头的件名。而后,对它们自动执行ls –l命令
$ find ~ -name “s*” -type f -exec ls -l {} \;
例:重复上一步的查找。不同的是,对查找的文件名执行ls –l命令时,采用与用户进行交互的方式。
$ find ~ -name “s*” -type f -ok ls -l {} \;
例:在/usr目录中,查找所有用户zino拥有的文件。而后,计算出这些文件的个数。并将所有的错误信息重定向到一个名为errfile的文件中。
$ find /usr/ -user zino -print 2> errfile | wc -l
6. vi编辑器
vi编辑器是UNIX的强有力的文本文件编辑工具,利用它可以建立、修改文本文件。
(1) vi编辑器的进入:
vi 文件名(自动进入命令方式)。
![image-20211030105126128](https://gitee.com/Zino00/img_bed/raw/master/img//20220126220055.png)
(2) vi编辑器的退出:
在指令模式下键入:q,:q!,:wq或:x(注意:号), 就会退出vi。其中:wq和:x是存盘退出,而:q是直接退出。
![image-20211030105205014](https://gitee.com/Zino00/img_bed/raw/master/img//20220126220056.png)
(3) 基本编辑指令
-
新增 (append)
- a :从光标所在位置后面开始新增资料。
- A: 从光标所在列最后面的地方开始新增加资料
-
插入 (insert)
- i: 从光标所在位置前面开始插入资料。
- I :从光标所在列的第一个非空白字元前面开始插入资料。
-
开始 (open)
- o :在光标所在列下新增一列并进入输入模式。
- O: 在光标所在列上方新增一列并进入输入模式。
- x: 删除光标所在字符。
- dd :删除光标所在的列。
- r :修改光标所在字元,r 后接著要修正的字符。
- R:进入取替换状态,新增文字会覆盖原先文字,直到按[ESC] 回到指令模式下为止。
- s: 删除光标所在字元,并进入输入模式。
- S: 删除光标所在的列,并进入输入模式。
7. grep命令
grep命令从指定的输入文件中查找与指定的正则表达式相匹配的行。默认情况下,grep命令将输出所找到
的匹配行。
grep命令的一般形式如下:grep [options] [regular-expression] [file1, file2, …]
其中,options指定grep命令的各种选项,regularexpression指定用来查找的正则表达式,file1,file2,…等参数是要查找的输入文件名。在最简单的情况下,用来查找的正则表达式可以是简单的字符串。
例:在文件/etc/passwd中,找出所有以nm开头的用户名所在的文本行。
$ grep ‘^nm’ /etc/passwd
例:在文件/etc/passwd中,找出所有以nologin结尾的文本行。
$grep ‘nologin$’ /etc/passwd
例:在文件/etc/passwd中,找出所有包含root的文体行,并将输出重定向到passwd.out文件当中
$grep ‘root’ /etc/passwd > passwd
8. sort命令
sort命令的功能是对文件中的各行进行排序,并结果显示在标准输出上。如不指定输入文件或使用“-”,则表示排序内容来自标准输入。
sort 命令的一般形式为:sort [options] file
例:以字母顺序显示/etc/passwd文件的内容。然后,以逆序显示其内容。
$ sort /etc/passwd
$ sort -r /etc/passwd
9. head命令与tail命令
例: 显示/etc/passwd文件的前10行的内容
$ head -10 /etc/passwd
例:显示/etc/passwd文件的前5行的内容
$ head -5 /etc/passwd
例:显示/etc/passwd文件的后10行的内容
$ tail -10 /etc/passwd
实验5 控制进程
一、实验目的
- 熟悉进程的操作与控制
- 掌握使用linux命令管理和操作进程的方法
二、实验要求
- 使用ps与jobs命令监视进程
- 使用kill与jobs命令控制进程
- 显示当前进程的ID
三、实验预备知识
1、多用户
LINUX操作系统的一个优点就是,它完全是作为一个多用户的系统设计的,LINUX操作系统提供了分时操作功能。多用户功能,不仅仅在于同时为多个用户提供计算支持,它还应包括为系统中的各个用户的数据提供保护方案。在多用户的支持下,用户们可以在同一台计算机上同时进行不同的操作,还可以同时协调完成同一任务。
2、多任务
多任务是指在同一时间内可以同时启动多个程序,各个程序同时运行,协调地使用系统资源。多任务的实现需要由操作系统来支持,不同程序的运行需要由操作系统来调度。从多任务实现的角度来说,多任务分为两类:协作式多任务和抢占式多任务。
3、进程
进程是操作系统中可以独立调度的单元,每个执行的任务都成为进程,可以看作是程序的一次执行
程序和进程的概念差别在于:程序是一些数据和指令的集合,它通常是以文件的形式存在,并且一旦程序生成后,除非删除它,否则它将一直存在。而进程是一个动态的概念,它是程序的一次执行。从系统的角度来说,进程是操作系统用于调度的基本单位,进程是可以独立拥有系统资源的单位。它具有几个特性:并行性、异步性、互斥性。
4、作业
作业(job)这个概念最早出现在批处理系统中。作业是用户向计算机系统提交一项工作的基本单位,是用户在一次事务处理或计算过程中要求计算机所做工作的总和。
5、进程和作业
作业是用户向计算机系统提交一项工作的基本单位,是用户在一次事务处理或计算过程中要求计算机所做工作的总和,是描述用户向系统提交工作任务的实体单位 。进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动,是操作系统分配资源和进行调度的基本单位,是系统完成工作任务时程序执行的实体单位。
6、前台和后台
前台进程是指正在对它进行交互操作的进程——它从键盘接收输入,并将输出送往屏幕(当然,用户可以对输入输出进行重新定向)。有一些进程需要很长时间才能结束,并且运行起来很单调乏味。编译程序就是一个这样的进程。在后台运行它们就可以了。
7、进程状态及转换
⑴ 进程的基本状态
⑵ linux系统进程状态
8、父子进程
进程在其执行过程中,能通过系统调用创建多个新进程。创建进程成为父进程,而新进程称为该进程的子进程。
9、命令简介
⑴ Ps(Process Status)
查看目前的系统中有哪些进程正在执行,以及它们的执行情况。
语法:ps [任选参数]
常用参数:
ps或ps -x 查看系统中,属于自己的进程。
ps -au 查看系统中,所有用户的进程。
ps -aux 查看系统中,包含系统内部的及所有用户的进程。
⑵ top
动态显示进程,实时监测进程状态。
常用操作:
q:退出top命令。
m:切换显示内存信息。
t:切换显示进程和CPU状态信息。
c:切换显示命令名称和完整命令行。
W:将当前设置写入~/.toprc文件中。这是写top配置文件的推荐方法。
⑶ kill
Kill命令可以传送的信号有很多种,但以SIGTERM(15)或SIGKILL(9)居多,它们都是用来结束进程执行的。
语法:kill [-9] PID
PID:利用ps 命令所查出的进程号。
例如:
kill 456或kill -9 456 终止进程号为456 的进程。
语法:kill %n
n:利用jobs命令查看出的后台作业号
结束或终止后台中的进程
⑷ &
在后台执行进程的方式
语法:命令&
⑸ jobs
显示当前会话的作业状态。
语法
jobs [ -l | -n | -p ] [ JobID … ]
⑹ 外壳变量
查看外壳变量的设定值
语法:set 查看所有外壳变量的设定值。
语法:echo $变量名显示指定的外壳变量的设定值。
设定外壳变量
语法:set var = value
删除外壳变量
语法:unset var
⑺ 查看系统中的用户
Who命令主要用于查看当前登录的用户情况。
语法: who [-imqsuwHT][- -count][- -heading][- -help][- -
message][- -mesg][- -version][- -writable][file][am i]
⑻查看当前系统上所有工作站的用户
语法: rusers
按Ctrl+D> 结束
四、实验内容
1. 进程的结构
(1) 登录进程系统,显示当前的进程ID
(2) 创建一个子shell
(3) 执行ls -R / > outfile2 2>> errfile &
命令后,立即再运行一个命令,列出你的所有正在运行的进程。
(4) 使用exit命令,中止你的子shell进程。如果再次使用exit命令,退出你的登录shell,此时,发生了什么?
一次exit后回到用户,连续用两次exit后终端关闭。
(5) 显示出当前进程环境中的所有变量
(6) 创建一个变量x,并将它的值赋为10,查看变量x的值,再次列出你的当前进程环境中的所有变量
我们发现x=10出现在了环境变量中
(7) 创建一个子shell。查看子shell中变量x的值。它的值是多少?列出子shell中所有的当前变量,有变量x吗?
子shell中变量x值为空,子shell中没有变量x
(8) 返回到父进程。将变量x的值传递给子进程。创建一个子shell,并且查看变量x的值。
子shell中变量x变成了10
(9) 在子shell中将200赋给变量x。查看变量的值是否已经改变了
变成了200
(10) 回到父进程中。查看当前环境中,变量x的值。子shell中对变量x的值的改变,上传到了父shell中吗
父进程仍然为10, 子shell对变量x的改变没有上传到父shell。
(11) 创建一个shell脚本,将其命名为sc1,它的内容应是:pwd ; cd / ; pwd
(12) 将文件sc1设置成可执行,而后运行该文件。你现在处于哪个目录中呢?为什么?
依旧在当前目录
因为用./方式运行脚本,就相当于启动了一个子进程,在 子进程里进入了根目录,但是当前进程仍在当前目录,脚本执行完的时候子进程结束了 ,会回到当前进程,也就是回到了当前目录。
(13) 创建另一个名为sc2的shell脚本。它的内容是:var1=hello;var2=$LOGNAME;export var1 var2
(14) 将文件sc2设置成可执行,而后运行该文件。运行结束后,查看变量var1与var2的值。var1与var2的值分别为多少?为什么?
值为空
因为用./方式运行脚本,就相当于启动了一个子进程,在脚本里设置的环境变量只在这个子进程里有效,脚本执行完的时候子进程结束了 脚本里的环境变量以及别的变量都会消失。
(15) 再次运行文件sc2。 此次,使用’.’命令,强迫该文件在当前shell中运行。运行结束后,查看变量var1与var2的值。现在var1与var2的值分别为多少?为什么?
. sc2会强制该文件在当前shell执行,所以设置的变量仍在当前shell进程。
2. 作业控制
(1) 在前台执行命令ls –R / > outfile 2> errfile
(2) 暂停这个你刚刚启动的作业
(3) 将上面暂停的作业转至后台继续运行
(4) 把这个作业转到前台来
(5) 当ls命令执行结束时,在后台重新启动它。显示出进程的ID,而后退出系统
(6) 重新登录进来,查看该后台进程是否仍在运行
没有在继续执行
(7) 创建一个包含如下内容的shell脚本文件sc3
将它置为可执行。使用nohup命令运行该文件,并将其放在后台运行(将输出重定向到sc3.out,将错误输出重定向到sc3err)。然后退出系统。
用ps打印当前正在执行的进程,记下进程号,然后关闭
(8) 再次登录进来,用top查看进程是否仍在运行
用top -p 12062查看指定的sleep进程发现进程仍在运行
(9) 进程完成后,显示输出文件outfile的内容
文件过大,无法完全显示
3. 结束一个进程
(1) 使用ls –R / 命令(将错误重定向,将输出重定向),在后台启动这个运行时间很长的作业。请记录该后台进程的ID号
(2) 在知道进程ID的情况下,杀死这个进程。确认你已经杀死了该进程
4. 查看用户信息
(1) 查看目前在系统中登录的所有用户清单
(2) 显示登陆者信息
实验6 C编程和调试
一、实验目的
- 使用户熟悉C编程工具gcc、gdb、make
- 熟练掌握gcc、gdb、make ,为后续项目作准备
二、实验要求
- 熟悉gcc编译四阶段,习惯gcc界面与查错,用gcc编译一个示例程序。
- 熟悉gdb常见调试命令,用gdb调试一个示例程序,熟悉调试环境。
- 熟悉make和makefile规则,通过查看一个示例程序makefile,学会用makefile管理自己的项目。
三、实验预备知识
1. Gcc
GNU C编译器(GCC)是一个在Unix或linux等系统上运行的功能强大的编译器,主要用于对C/C++/Object C等语言的编译。
(1) GCC提供的各种程序以及各个头文件的位置
- /usr/lib/gcc-lib/target/version/及其子目录 ,大部分的编译器都被放在这个地方 。
- /usr/bin/gcc 这里装的是编译器的驱动程序。
- /lib与/usr/lib 这两个目录与另外的一些目录都是本地系统的程序库目录。
- /usr/include/及其子目录 这些目录下的头文件大部分都是由libe套件(libe binary package)所提供的。
(2) GCC用法
使用GCC时通常在其后面跟上一些编译选项以及要编译的文件名,以下给出的是GCC的基本用法:gcc [option] [filename]
(3) GCC的主要选项
- -x language 指定使用的语言(c、c++或汇编);
- -c 只对文件进行编译和汇编,但不连接;
- -S 只对文件进行编译,但不汇编和连接;
- -E 只对文件进行预处理,但不编译、汇编和连接;
- -O[file1] file2 将文件file2编译成可执行文件file1;
- -l library 用来指定所使用的库文件;
- -I directory 为include文件的搜索指定目录;
- -W 禁止警告信息
- -pedantic 严格要求符合ANSI标准;
- -Wall 显示附加的警告信息。
- -g 显示排错信息以用于gdb;
- -p 产生prof所需的信息;
- -pg 产生gprof 所使用的信息;
2. Gdb
gdb是一个用来调试c和c++程序的功能比较强大的调试器。
(1) 在gdb中有关调试过程中显示数据的命令有以下几条
- display命令,用来显示一些表达式的值。
- info display命令,用来显示当前所有的要显示值的表达式的有关情况。
- delete display命令 用来删除一个要显示值的表达式 。
- disable display 使一个要显示值的表达式暂时无效,但并不删除该表达式的显示。
- enable display 它与disable display命令相反,使显示值被屏蔽的表达式恢复显示 。
- undisplay 这个命令用来结束某个表达式值的显示,它的功能和delete display基本一致 。
- whatis 用来显示某个表达式的数据类型。
- set命令 用来为变量赋值。
(2) gdb中有关文件的命令
- add-shared-symbol-files 用来从动态的连接映射的共享目标文件中装入符号表。
- add-symbol-file 用来从已经动态装入的文件中装入符号表。
- cd 用来改变当前工作目录,和shell里的cd命令是一样的。
- core-file 使某个文件成为core dump,从而可以检验内存和寄存器。
- directory 用来向源文件搜索路径中增加一个目录。
- pwd pwd命令和shell中的pwd命令的功能是一样的,二者都是用来显示当前工作路径的。
(3) gdb中有关程序运行的命令
- cont cont命令和continue命令的功能是一样的,它使程序在信号发生后或是停在断点之后再继续运行。
- handle 用来对信号设置处理函数。
- jump 指定程序开始调试的指令或地址。
- kill kill命令用来结束当前程序的调试。
- next是用来继续程序的运行。
- nexti 用来单步执行一条指令的。
- step用来执行一条语句。
- stepi 用来执行一条指令。
3. make
make命令通过makefile文件来描述源程序之间的相互关系并自动维护编译工作。
make命令本身可带有四种参数:标志、宏定义、描述文件名和目标文件名。
其标准形式为:make [flags] [macro definitions] [targets]。
四、实验内容
1. Gcc
(1) 用熟悉的编辑器输入清单1所示的代码:
清单1:
//hello.c
#include <stdio.h>
int main(void)
{
printf ("Hello world, Linux programming!\n");
return 0;
}
然后执行下面的命令编译和运行这段程序:
$ gcc hello.c -o hello
$./hello
使用GCC编译程序时,编译过程可被细分为四个阶段 :
预处理(Pre-Processing) # gcc -E hello.c -o hello.i
编译(Compiling) # gcc -c hello.i -o hello.o
汇编(Assembling)
链接(Linking) # gcc hello.o -o hello
(2) 使用gcc发现程序错误
清单2:
//illcode.c
#include <stdio.h>
void main(void)
{
long long int var = 1;
printf("It is not standard C code!\n");
}
当GCC在编译不符合ANSI/ISO C语言标准的源代码时,如果加上了-pedantic选项,那么使用了扩展语法的地方将产生相应的警告信息:
$ gcc -pedantic illcode.c -o illcode
illcode.c: In function ‘main’:
illcode.c:9: ISO C89 does not support ‘long long’
illcode.c:8: return type of ‘main’ is not ‘int’
2. Gdb
下面用一个实例介绍怎样用gdb调试程序。程序清单见教程。
用gcc编译它:gcc -g -o test test.c
程序执行时显示如下结果:
The string is hello there
The string printed backward is
输出的第一行是正确的,但第二行打印出并不是我们所期望的。my_print2 函数没有正常工作。现在,让我们用gdb看看问题究竟出在哪儿,先输入如下命令:
gdb greeting。
如果输入命令时忘了把要调试的程序作为参数传给gdb,可以在gdb提示符下用file命令加载它:(gdb) file greeting。这个命令加载greeting可执行文件,就像在gdb命令行里加载它一样
选择自己的test.c文件调试
-g 加入调试信息并进入调试界面: 直接运行:
加入断点并查看断点:
用run执行,并用next查看下一条指令:
用step深入函数内部:
用list查看函数代码:
用delete 1 删除断点并用info break重新查看断点:
用print查看某一变量具体的值:
用x查看某一内存地址具体的值:
用disas反汇编某一函数:
3. make
在Linux系统中,专门提供了一个make命令来自动维护目标文件。
以下是一个简单的Makefile的简单例子:
prog:prog1.o prog2.o
gcc prog1.o prog2.o -o prog
prog1.o:prog1.c lib.h
gcc -c -I. -o prog1.o prog1.c
prog2.o:prog2.c
gcc -c prog2.c
测试例子
makefile
test: test1.o test2.o
gcc -Wall test1.o test2.o -o test
test1.o: test1.c test2.h
gcc -c -Wall test1.c -o test1.o
test2.o: test2.c test2.h
gcc -c -Wall test2.c -o test2.o
clean:
rm -rf *.o test
//test1.h
#include<stdio.h>
#include "test2.h"
int main()
{
printf("This is test1!\n");
PrintTest2();
return 0;
}
//test2.h
#include<stdio.h>
#include "test2.h"
void PrintTest2()
{
printf("This is test2!\n");
}
//test2.h
#ifndef TEST2_H_
#define TEST2_H_
void PrintTest2();
#endif
#使用缩写的Makefile
prog:prog1.o prog2.o
gcc prog1.o prog2.o -o $@
prog1.o:prog1.c lib.h
gcc -c -I. -o $@ $<
prog2.o:prog2.c
gcc -c $*.c
#使用缩写和宏的Makefile
MARCO = prog1.o prog2.o
prog:$(MARCO)
gcc prog1.o prog2.o -o $@
prog1.o:prog1.c lib.h
gcc -c -I. -o $@ $<
prog2.o:prog2.c
gcc -c $*.c