428
1 Linux 操操操操操操

Linux 操作系统

  • Upload
    skip

  • View
    208

  • Download
    0

Embed Size (px)

DESCRIPTION

Linux 操作系统. 吴翔虎. 第一章 Linux 概述. Linux 是一套免费使用和自由传播的类 Unix 操作系统,它主要用于基于 Intel x86 系列 CPU 的计算机上。其目的是建立不受任何商品化软件的版权制约的、全世界都能自由使用的 Unix 兼容产品。 Linux 最早由一位名叫 Linus Torvalds 的计算机爱好者开发,他的目的是设计一个代替 Minix 的操作系统,这个操作系统可用于 386 、 486 或奔腾处理器的个人计算机上,并且具有 Unix 操作系统的全部功能。. Linux 概貌. - PowerPoint PPT Presentation

Citation preview

Page 1: Linux 操作系统

1

Linux 操作系统

吴翔虎

Page 2: Linux 操作系统

2

第一章 Linux 概述 Linux 是一套免费使用和自由传播的类 Unix 操作

系统,它主要用于基于 Intel x86 系列 CPU 的计算机上。其目的是建立不受任何商品化软件的版权制约的、全世界都能自由使用的 Unix 兼容产品。

Linux 最早由一位名叫 Linus Torvalds 的计算机爱好者开发,他的目的是设计一个代替 Minix 的操作系统,这个操作系统可用于 386 、 486 或奔腾处理器的个人计算机上,并且具有 Unix 操作系统的全部功能。

Page 3: Linux 操作系统

3

Linux 概貌 Linux 以它的高效性和灵活性著称。它能够在个人

计算机上实现全部的 Unix 特性,具有多任务、多用户的能力。 Linux 可在 GNU (“不是 UNIX” 工程的缩写)公共许可权限下免费获得,是一个符合 POSIX 标准的操作系统。 Linux 操作系统软件包不仅包括完整的 Linux 操作系统,而且还包括了文本编辑器、高级语言编译器等应用软件。它还包括带有多个窗口管理器的 X-Windows 图形用户界面,如同我们使用 Windows NT 一样,允许我们使用窗口、图标和菜单对系统进行操作。

Page 4: Linux 操作系统

4

Linux 概貌 Linux 之所以受到广大计算机爱好者

的喜爱,主要原因有两个,一是它属于自由软件,用户不用支付任何费用就可以获得它和它的源代码,并且可以根据自己的需要对它进行必要的修改和无约束地继续传播。另一个原因是,它具有 Unix 的全部功能,任何使用 Unix 操作系统或想要学习 Unix 操作系统的人都可以从 Linux中获益。

Page 5: Linux 操作系统

5

Linux 的主要特点 开放性:指系统遵循世界标准规范,特别是遵循开放系统互

连( OSI )国际标准。 多用户:是指系统资源可以被不同用户使用,每个用户对自

己的资源(例如:文件、设备)有特定的权限,互不影响。

多任务:它是指计算机同时执行多个程序,而且各个程序的

运行互相独立。 良好的用户界面 : Linux 向用户提供了两种界面:用

户界面和系统调用。 Linux 还为用户提供了图形用户界面。它利用鼠标、菜单、窗口、滚动条等设施,给用户呈现一个直观、易操作、交互性强的友好的图形化界面。

Page 6: Linux 操作系统

6

Linux 的主要特点 设备独立性:是指操作系统把所有外部设备统一当作成文件来看

待,只要安装它们的驱动程序,任何用户都可以象使用文件一样,操纵、使用这些设备,而不必知道它们的具体存在形式。 Linux 是具有设备独立性的操作系统,它的内核具有高度适应能力

提供了丰富的网络功能:完善的内置网络是 Linux 一大特点。

可靠的安全系统: Linux采取了许多安全技术措施,包括对读、写控制、带保护的子系统、审计跟踪、核心授权等,这为网络多用户环境中的用户提供了必要的安全保障。

良好的可移植性:是指将操作系统从一个平台转移到另一个平台使它仍然能按其自身的方式运行的能力。 Linux 是一种可移植的操作系统,能够在从微型计算机到大型计算机的任何环境中和任何平台上运行。

Page 7: Linux 操作系统

7

Linux 的组成 LINUX 的内核:内核是系统的核心,是运行程序和管理

像磁盘和打印机等硬件设备的核心程序。 LINUX SHELL: Shell 是系统的用户界面,提供了用户与

内核进行交互操作的一种接口。 LINUX 文件系统 : Linux 文件系统是文件存放在磁盘等

存储设备上的组织方法。 Linux 能支持多种目前浒的文件系统,如EXT2、 EXT3、 FAT 、 VFAT 、 ISO9660、 NFS 、 SMB等。

LINUX 应用系统:标准的 Linux 系统都有一整套称为应用程序的程序集,包括文本编辑器、编程语言、X Window 、办公套件、 Internet工具、数据库等。

Page 8: Linux 操作系统

8

Linux 的主要版本1. 红旗 Linux

2. 冲浪 Linux

3. 中软 Linux

4. Red Hat Linux

5. Mandrake Linux

6. TurboLinux

Page 9: Linux 操作系统

9

Linux 的系统结构

硬件

内核

nroff sh

who

a. out

date

wc

grep

edvi

l d

as

comp

cpp

cc

其他应用程序

其他应用程序

Page 10: Linux 操作系统

10

Linux 文件系统

/

f s1 bi n etc usr uni x dev

mj b maury sh date who passwd src bi n tty00 ttyo1

cmd

date. c who. c

Page 11: Linux 操作系统

11

文件系统的特点• 层次结构• 文件数据的一致对待• 建立、删除文件的能力• 文件的动态增长• 文件数据的保护• 把外围设备(如终端及磁盘等)作为文件

看待

Page 12: Linux 操作系统

12

处理环境1 、程序与进程

– 程序是一个可执行文件,– 进程是一个执行中的程序的实例;

2 、子进程与父进程 一个进程可通过系统调用 fork创建一个新进程,

称创建的进程为子进程,而创建它的进程称为父进程。

3 、系统调用的执行— shell 命令解释程序 shell 允许三种类型的命令:

– 可执行文件;– 包含一系列 shell命令的可执行文件;– 一个内部 shell命令。

Page 13: Linux 操作系统

13

Linux 操作系统的服务• 通过允许进程的创建、终止、挂起及通信

来控制进程的执行 • 对进程在 CPU 上的执行进行公平调度• 对正在执行的进程分配主存• 为实现用户数据的有效存储和检索而分配二级存储

• 允许进程对 I/ O 设备进行有控制的存取

Page 14: Linux 操作系统

14

Linux 对硬件的要求1 、用户进程的执行状态:用户态和核心态2 、中断与异常

– 中断:允许 I/ O 外设或系统时钟异步终端 CPU

– 例外:指的是有一个进程引起的非期望事件,如:非法存储寻找,执行特权指令等

3 、存储管理–核心永远驻留在主存中– 编译程序产生的是虚地址,核心与机器硬件一起协作,建立虚地址到物理地址的转换

Page 15: Linux 操作系统

15

第二章 Shell 及常用命令 shell 是一种命令解释器,它提供了用户

和操作系统之间的交互接口。 shell 是面向命令行的,而 X Window 则是图形界面。你在命令行输入命令, shell 进行解释,然后送往操作系统执行。 shell 可以执行 Linux 的系统内部命令,也可以执行应用程序。你还可以利用外壳编程,执行复杂的命令程序。 shell 也可以说是一种程序设计语言。

Page 16: Linux 操作系统

16

Shell 的类型 Linux 提供几种 shell 程序以供选择。

常用的有 Bourne shell( b s h ) 、 C shell( c s h ) 和 Korn shell( k s h ) 。各个 shell都能提供基本的功能,又有其各自的特点。

• Bourne shell 是由 Steven Bourne 编写的,是 UNIX 的缺省 shell 。 Bourne shell 编程能力很强。但它不能处理命令的用户交互特征。 bash 是 Bourne 外壳的增强版。

Page 17: Linux 操作系统

17

Shell 的类型• C shell 是由加利福尼亚大学伯克利分校的 Bill Joy 编写

的。它能提供 Bourne shell 所不能处理的用户交互特征,如命令补全、命令别名、历史命令替换等。很多人认为, C shell 的编程能力不如 Bourne shell ,但它的语法和 C语言类似,所以 C 程序员将发现 C shell 很顺手。 tcsh 是 C shell 的增强版本和 C shell 完全兼容。

• K o r n shell 是由 Dave Korn 编写的。 Korn shell 融合了 C shell 和 Bourne shell 的优点,并和 Bourne shell 完全兼容。 Korn shell 的效率很高,其命令交互界面和编程交互界面都很不错。 Public Domain Korn shell( p d k s h ) 是 Korn shell 的增强版。

Page 18: Linux 操作系统

18

bash shell

bash 是大多数 L i n u x 系统的缺省外壳。它克服了 Bourne 外壳的缺点,又和 Bourne 外壳完全兼容。 ba s h 有以下的特点:

• 补全命令行。当你在 bash 命令提示符下输入命令或程序名时,你不必输全命令或程序名,按 Tab 键, b a s h 将自动补全命令或程序名。

• 通配符。在 b a s h 下可以使用通配符 * 和?。 *可以替代多个字符,而?则替代一个字符。

Page 19: Linux 操作系统

19

bash shell

• 历史命令。 bash 能自动跟踪你每次输入的命令,并把输入的命令保存在历史列表缓冲区。缓冲区的大小由 HISTSIZE 变量控制。当你每次登录后,home 目录下的 .bash_history 文件将初始化你的历史列表缓冲区。你也能通过 history 和 fc 命令执行、编辑历史命令。

• 别名。在 b a s h 下,可用 alias 和 unalias 命令给命令或可执行程序起别名和清除别名。这样你可以用自己习惯的方式输入命令。

Page 20: Linux 操作系统

20

bash shell

• 输入 /输出重定向。输入重定向用于改变命令的输入,输出重定向用于改变命令的输出。输出重定向更为常用,它经常用于将命令的结果输入到文件中,而不是屏幕上。输入重定向的命令是 < ,输出重定向的命令是 > 。

• 管道。管道用于将一系列的命令连接起来。也就是把前面的命令的输出作为后面的命令的输入。管道的命令是 | 。

Page 21: Linux 操作系统

21

bash shell

• 提示符。 bash 有两级提示符。第一级提示符就是你登录外壳时见到的,缺省为 $ 。你可以通过重新给 p s 1 变量赋值来改变第一级提示符。当 b a s h需要进一步提示以便补全命令时,会显示第二级提示符。第二级提示符缺省为> ,你可以通过重新给 p s 2 变量赋值来改变第二级提示符。一些特殊意义的字符也可以加入提示符赋值中。

• 作业控制。作业控制是指在一个作业执行过程中,控制执行的状态。你可以挂起一个正在执行的进程,并在以后恢复该进程的执行。按下 Ctrl+Z 挂起正在执行的进程,用b g 命令使进程恢复在后台执行,用 f g 命令使进程恢复在前台执行。

Page 22: Linux 操作系统

22

登陆和退出 Linux 启动后,给出 login 命令,等待用户登录。 Login: < 输入用户名 >

Password: < 输入密码 >

如果是正确的用户名和密码,那么你就会进入 Linux 的 shell , shell 给出命令提示符,等待你输入命令(不要随意以 r o o t身份登录,以避免对系统造成意外的破坏)。

使用 l o g o u t 命令退出外壳。

Page 23: Linux 操作系统

23

更改账号密码语法: p a s s w d

Old password: < 输入旧密码 >

New password: < 输入新密码 >

Retype new password: <再输入一次密码 >

Page 24: Linux 操作系统

24

联机帮助语法: man 命令例如 : man ls

Page 25: Linux 操作系统

25

远程登录语法: rlogin 主机名 [-1 用户名 ]

例如:rlogin doc 远程登录到工作站 doc 中。rlogin doc -l user 使用 user 帐号登录到工作站 doc

中。语法: telnet 主机名或 telnet IP地址例如:telnet doc

telnet 140.109.20.251

Page 26: Linux 操作系统

26

文件或目录处理列出文件或目录下的文件名。语法: ls [-atFlgR] [name]

name :文件名或目录名。

例如:ls 列出目前目录下的文件名。ls -a 列出包括以.开始的隐藏文件的所有文件名。ls -t 依照文件最后修改时间的顺序列出文件名。

Page 27: Linux 操作系统

27

文件或目录处理ls -F 列出当前目录下的文件名及其类型。以 / 结尾 表示为目录名,以 * 结尾表示为可执行文件, 以 @ 结尾表示为符号连接。ls -l 列出目录下所有文件的权限、所有者、文件大 小、修改时间及名称。ls -lg 同上,并显示出文件的所有者工作组名。ls -R 显示出目录下以及其所有子目录的文件名。

Page 28: Linux 操作系统

28

改变工作目录语法: cd [name]

n a m e :目录名、路径或目录缩写。

例如 :

cd 改变目录位置至用户登录时的工作目录。cd dir1 改变目录位置至 d i r 1 目录下。

Page 29: Linux 操作系统

29

改变工作目录cd ~user 改变目录位置至用户的工作目录。cd .. 改变目录位置至当前目录的父目录。cd ../user 改变目录位置至相对路径 user 的目录下。cd /../.. 改变目录位置至绝对路径的目录位置下。cd ~ 改变目录位置至用户登录时的工作目录。

Page 30: Linux 操作系统

30

复制文件语法 : cp [-r] 源地址目的地址

例如:cp file1 file2 将文件 file1 复制成 f i l e 2 。cp file1 dir1 将文件 file1 复制到目录 dir1 下,文

件名 仍为 f i l e 1 。

Page 31: Linux 操作系统

31

复制文件cp /tmp/file1 . 将目录 /tmp 下的文件 file1 复制到当前

目录下,文件名仍为 f i l e 1 。cp /tmp/file1 file2 将目录 /tmp 下的文件 file1 复制到

当前目录下,文件名为 f i l e 2 。cp -r dir1 dir2 复制整个目录。

Page 32: Linux 操作系统

32

移动或更改文件、目录名称语法: mv 源地址目的地址

例如:mv file1 file2 将文件 f i l e 1更名为 f i l e 2 。mv file1 dir1 将文件 f i l e 1移到目录 dir1 下,文

件名 仍为 f i l e 1 。mv dir1 dir2 将目录 dir1 更改为目录 d i r 2 。

Page 33: Linux 操作系统

33

建立新目录语法: mkdir 目录名

例如:mkdir dir1 建立一新目录 d i r 1 。

Page 34: Linux 操作系统

34

删除目录语法: rmdir 目录名或 rm 目录名

例如:rmdir dir1 删除目录 d i r 1 ,但 dir1 下必须没有文

件 存在,否则无法删除。rm -r dir1 删除目录 d i r 1 及其子目录下所有文件。

Page 35: Linux 操作系统

35

删除文件语法: rm 文件名

例如:rm file1 删除文件名为 file1 的文件。rm file? 删除文件名中有五个字符且前四个字符为 file 的所有文件。rm f* 删除文件名中以 f 为字首的所有文件。

Page 36: Linux 操作系统

36

列出当前目录语法: p w d

Page 37: Linux 操作系统

37

查看文件内容语法: cat 文件名

例如:cat file1 以连续显示方式,查看文件名 file1 的内容。

Page 38: Linux 操作系统

38

分页查看文件内容语法: more 文件名或 cat 文件名 | more

例如:more file1 以分页方式查看文件名 file1 的内容。cat file1 | more 以分页方式查看文件名 file1 的内容。

Page 39: Linux 操作系统

39

查看目录所占磁盘容量语法 : du [-s] 目录例如 :

du dir1 显示目录 dir1 的总容量及其子目录的容量( 以 KB 为单位 ) 。du -s dir1 显示目录 dir1 的总容量。

Page 40: Linux 操作系统

40

文件传输1. 拷贝文件或目录至远程工作站语法: rcp [-r] 源地址主机名 : 目的地址例如:rcp file1 doc:/home/user 将文件 f i l e 1拷贝到工作站

doc 路径 /home/user 下。rcp -r dir1 doc:/home/user 将目录 d i r 1拷贝到工

作站 doc 路径 /home/user 下。

Page 41: Linux 操作系统

41

文件传输2. 自远程工作站,拷贝文件或目录语法: rcp [-r] 主机名 :源地址目的地址例如:rcp doc:/home/user/file1 file2 将工作站 d o c路径/home/user 下的文件 file 1 ,拷贝到当前工作站的目录下,文件名为 file 2 。rcp -r doc:/home/user/dir1 . 将工作站 doc 路径/home/user 下的目录 d i r 1 ,拷贝到当前工作站的

目录下,目录名仍为 d i r 1 。

Page 42: Linux 操作系统

42

文件传输3. 本地工作站与远程工作站之间的文件传输必须拥有远程工作站的帐号及密码,才可进行传输工作。语法: ftp 主机名或 ftp ip地址例如:ftp doc 与远程工作站 doc 之间进行文件传输。Name (doc:user-name): < 输入帐号 >

Password (doc:user-password): < 输入密码 >

ftp> help 列出 ftp 文件传输时可使用的命令。ftp> !ls 列出本地工作站当前目录下的所有文件名。ftp> !pwd 列出本地工作站当前所在的目录位置。ftp> ls 列出远程工作站当前目录下的所有文件名。

Page 43: Linux 操作系统

43

文件传输ftp> dir 列出远程工作站当前目录下的所有文件名。ftp> dir . |more 分页列出远程工作站当前目录下的所有文件 名。ftp> pwd 列出远程工作站当前所在的目录位置。ftp> cd dir1 更改远程工作站的工作目录位置至 dir1 之下。ftp> get file1 将远程工作站的文件 f i l e 1拷贝到本地工作站 中。ftp> put file2 将本地工作站的文件 f i l e 2拷贝到远程工作站 中。ftp> mget *.c 将远程工作站中扩展文件名为 c 的所有文件拷 贝到本地工作站中。

Page 44: Linux 操作系统

44

文件传输ftp> mput *.txt 将本地工作站中扩展文件名为 txt 的所有文

件 拷贝到远程工作站中。ftp> prompt 切换交互式指令 ( 使用 mput/mget 时不是每个

文 件皆询问 y e s / n o ) 。ftp> quit 结束 ftp 工作。ftp> bye 结束 ftp 工作。

注意从 PC 与工作站间的文件传输也可透过在 PC端的 FTP指

令进行文件传输,指令用法与上述指令大致相同。

Page 45: Linux 操作系统

45

文件权限设定1. 改变文件或目录的读、写、执行权限

语法: chmod [-R] mode name

n a m e : 文件名或目录名。mode: 3 个 8 位数字或 r w x 的组合。 r- r e a d (读 ) , w - w r i t e ( 写 )

x - e x e c u t e ( 执行 )

u - u s e r ( 当前用户 ) , g - g r o u p(组) o - o t h e r(其他用户)。

Page 46: Linux 操作系统

46

文件权限设定chmod 755 dir1 对于目录 d i r 1 ,设定成任何使用

者皆有读取及执行的权利,但只有所有者可做修改。chmod 700 file1 对于文件 f i l e 1 ,设定只有所有者可以读、写和执行的权利。chmod u+x file2 对于文件 f i l e 2 ,增加当前用户

可以执行的权利。chmod g+x file3 对于文件 f i l e 3 ,增加工作组使

用者可执行的权利。chmod o-r file4 对于文件 f i l e 4 ,删除其他使用

者可读取的权利。

Page 47: Linux 操作系统

47

文件权限设定2.改变文件或目录的所有权语法: chown [-R] 用户名 name

n a m e :文件名或目录名。

例如:chown user file1 将文件 file1 改为用户 user 所有。chown -R user dir1 将目录 d i r 1 及其子目录下面

的所有文件改为用户 user 所有。

Page 48: Linux 操作系统

48

检查自己所属的工作组语法: g r o u p s

Page 49: Linux 操作系统

49

改变文件或目录工作组所有权语法: chgrp [-R] 工作组名 name

n a m e :文件名或目录名

例如:chgrp vlsi file1 将文件 file1 的工作组所有权改为 vl

si

工作组所有。

chgrp -R image dir1 将目录 d i r 1 及其子目录下面的

所有文件,改为 image 工作组所有。

Page 50: Linux 操作系统

50

改变文件或目录最后修改时间语法: touch name

n a m e :文件名或目录名。

Page 51: Linux 操作系统

51

文件的链接同一文件,可拥有一个以上的名称,也就是把一个文件进行链接。

语法: ln 老文件名 新文件名

例如:ln file1 file2 将文件 f i l e 2链接至文件 f i l e 1 。

Page 52: Linux 操作系统

52

文件中字符串查寻语法: grep string file

例如:grep abc file1 寻找文件 f i l e 1 中包含字符串 abc

所在行的文本内容。

Page 53: Linux 操作系统

53

寻找文件或命令的路径语法: whereis command 显示命令的路径。语法: which command 显示命令的路径,及使用

者 所定义的别名。语法: whatis command 显示命令功能的摘要。语法: find search-path -name filename -print 搜寻 指定路径下某文件的路径。

例如:find / -name file1 -print 自根目录下寻找文件 file1

的路径。

Page 54: Linux 操作系统

54

比较文件或目录的内容语法: d i ff [-r] name1 name2

name1 name2 :可同时为文件名或目录名。

例如:d i ff file1 file2 比较文件 file1 与 file2 内各行的不同

之处。d i ff -r dir1 dir2 比较目录 dir1 与 dir2 内各文件的

不同之处。

Page 55: Linux 操作系统

55

文件打印输出用户可用 set 命令来设定打印机名。例如:set -a PRINTER=sp 设定自 sp 打印机打印资料。

语法: lpr [-P打印机名 ] 文件名

例如:lpr file1 或 lpr -Psp file1 自 s p打印机打印文件 f i l

e

1 。

Page 56: Linux 操作系统

56

文件打印输出语法: enscript [-P打印机名 ] 文件名

例如:enscript file3 或 enscript -Psp file3 自 s p打印机打印

文件 f i l e 3 。

Page 57: Linux 操作系统

57

打印机控制命令1.检查打印机状态、打印作业顺序号和用户名

语法: lpq [-P 打印机名 ]

例如:lpq 或 lpq -Psp 检查 sp 打印机的状态。

Page 58: Linux 操作系统

58

打印机控制命令2. 删除打印机内的打印作业 ( 用户仅可删除自己的 打印作业 )

语法: lprm [-P打印机名 ] 用户名或作业编号

例如:lprm user 或 lprm -Psp user 删除 s p打印机中用户user 的打印作业,此时用户名必须为 u s e r 。lprm -Psp 456 删除 sp 打印机上编号为 456 的打印

作业。

Page 59: Linux 操作系统

59

进程控制命令1.查看系统中的进程语法: ps [-aux]

例如 :

p s 或 ps -x 查看系统中,属于自己的进程。ps -au 查看系统中,所有用户的进程。ps -aux 查看系统中,包含系统内部的及所有用户的进程。

Page 60: Linux 操作系统

60

进程控制命令2. 结束或终止进程语法: kill [-9] PID

P I D :利用 ps 命令所查出的进程号。

例如 :

kill 456 或 kill -9 456 终止进程号为 456 的进程。

Page 61: Linux 操作系统

61

进程控制命令3. 在后台执行进程的方式语法:命令 &

例如 :

cc file1.c & 将编译 file1.c 文件的工作置于后台行。

Page 62: Linux 操作系统

62

进程控制命令语法:按下 C o n t r o l + Z 键,暂停正在执行的进 程。键入 b g 命令,将暂停的进程置于后台继

续执行。

例如 :

cc file2.c

^ Z

S t o p p e d

b g

Page 63: Linux 操作系统

63

进程控制命令4. 查看正在后台中执行的进程语法: j o b s

Page 64: Linux 操作系统

64

进程控制命令5. 结束或终止后台中的进程语法: kill %n

n :利用 j o b s 命令查看出的后台作业号

例如:kill % 终止在后台中的第一个进程。kill %2 终止在后台中的第二个进程。

Page 65: Linux 操作系统

65

shell 变量1. 查看外壳变量的设定值语法: set 查看所有外壳变量的设定值。

语法: echo $ 变量名显示指定的外壳变量的设定值。

Page 66: Linux 操作系统

66

shell 变量2. 设定外壳变量语法: set var = value

例如 :

set term=vt100 设定外壳变量 t e r m 为 VT100 型终端。

Page 67: Linux 操作系统

67

shell 变量3. 删除外壳变量语法: unset var

例如 :

unset PRINTER 删除外壳变量 PRINTER 的设定值。

Page 68: Linux 操作系统

68

环境变量1. 查看环境变量的设定值语法: s e t 查看所有环境变量的设定值。

语法: echo $NAME 显示指定的环境变量 N A M E

的设定值。

例如 :

echo $PRINTER 显示环境变量 PRINTER 的设定值。

Page 69: Linux 操作系统

69

环境变量2. 设定环境变量语法: set –a NAME=word

例如 :

set -a PRINTER=sp 设定环境变量 PRINTER 为sp 。

Page 70: Linux 操作系统

70

环境变量3. 删除环境变量语法: unset NAME

例如 :

unset PRINTER 删除环境变量 P R I N T E R 的设定值。

Page 71: Linux 操作系统

71

别名1. 查看所定义的命令的别名语法: a l i a s 查看自己目前定义的所有命令,及 所对应的别名。

语法: alias name 查看指定的 name 命令的别名。

例如 :

alias dir 查看别名 dir 所定义的命令。ls -atl

Page 72: Linux 操作系统

72

别名2. 定义命令的别名语法: alias name‘command line’

例如 :

alias dir ‘ls -l’ 将命令 ls - l 定义别名为 d i r 。

Page 73: Linux 操作系统

73

别名3. 删除所定义的别名语法: unalias name

例如:unalias dir 删除别名 dir 的定义。unalias * 删除所有别名的设定。

Page 74: Linux 操作系统

74

历史命令1. 设定命令记录表的长度语法: set history = n

例如 :

set history = 40 设定命令记录表的长度为 40 ( 可记录

执行过的前面 40 个命令 ) 。

Page 75: Linux 操作系统

75

历史命令2. 查看命令记录表的内容语法: h i s t o r y

Page 76: Linux 操作系统

76

历史命令3. 使用命令记录表语法: !! 重复执行前一个命令。语法: ! n

Page 77: Linux 操作系统

77

文件压缩1. 压缩文件语法: compress 文件名 压缩文件语法: compressdir 目录名 压缩目录

2. 解压缩文件语法: uncompress 文件名 解压缩文件语法: uncompressdir 目录名 解压缩目录

Page 78: Linux 操作系统

78

重定向1. 标准输入的控制语法:命令 < 文件将文件做为命令的输入。

例如:mail -s “mail test” [email protected] < fil

e1

将文件 file1 当做信件的内容,主题名称为 mail

test ,送给收信人。

Page 79: Linux 操作系统

79

重定向2. 标准输出的控制语法:命令 > 文件将命令的执行结果送至指定的文 件中。

例如 :

ls -l > list 将执行“ ls -l” 命令的结果写入文件 list

中。

Page 80: Linux 操作系统

80

重定向语法:命令 >! 文件将命令的执行结果送至指定的文 件中,若文件已经存在,则覆盖。

例如:ls -lg >! list 将执行“ ls - lg” 命令的结果覆盖写入

文件 list 中。

Page 81: Linux 操作系统

81

重定向语法:命令 >& 文件将命令执行时屏幕上所产生的 任何信息写入指定的文件中。例如:cc file1.c >& error 将编译 file1.c 文件时所产生的任何信息写入文件 error 中。

Page 82: Linux 操作系统

82

重定向语法:命令 >> 文件将命令执行的结果附加到指定的文件 中。例如 :

ls - lag >> list 将执行“ ls - lag” 命令的结果附加到文件 list

中。语法:命令 >>& 文件将命令执行时屏幕上所产生的任何信息

附加到指定的文件中。例如 :

cc file2.c >>& error 将编译 file2.c 文件时屏幕所产生的任何信息附加到文件 error 中。

Page 83: Linux 操作系统

83

管道命令语法:命令 1 | 命令 2 将命令 1 的执行结果送到命

令 2 ,做为命令 2 的输入。

例如:ls -Rl | more 以分页方式列出当前目录及其子目录

下 所有文件的名称。cat file1 | more 以分页方式列出文件 file1 的内容。

Page 84: Linux 操作系统

84

重定向和管道命令ps | sort | moreps | sort | more > output1.txtkill –1 1234 > output1.txt 2>&1kill –1 1234 > /dev/null 2>&1

Page 85: Linux 操作系统

85

第三章 shell 程序设计• 交互式程序。 顺序地敲入一系列命令,让 shell 交互地执行它们。• 脚本程序( shell script ) 编写 shell脚本文件,并执行。

Page 86: Linux 操作系统

86

交互式程序$ for file in *> do> if grep –l POSIX $file> then> more $file> fi> done

Page 87: Linux 操作系统

87

关于通配符• * 可以替代多个字符,而?则替代一个字符。• [set] 匹配方括号中任何一个单个的字符。如: ls –l [Yy]* 列出当前目录下所有以 y和 Y开头的文件。

• {} 匹配花括号中的任何一个字符串。如: ls my_{finger toe}s 列出my_fingers 和my_toes两个文件。

Page 88: Linux 操作系统

88

脚本程序#!/bin/sh# first.sh# This file looks through all the files in t

he current# directory for the string POSIX, and then displays those# files to the standard output.

Page 89: Linux 操作系统

89

脚本程序For file in *do if grep –q POSIX $file then more $file fiDone

exit 0

Page 90: Linux 操作系统

90

运行脚本程序• chmod +x first.sh 给所有用户添加执行权限• ./first.sh

Page 91: Linux 操作系统

91

shell 程序设计的语法• 变量:字符串、数字、环境和参数• 条件: shell 中的布尔值• 程序控制: if 、 elif 、 for 、 while 、 until 、 cas

e 等• 命令表• 函数• 内建在 shell 中的命令• 获取某个命令的执行结果• 即时文档( here 文档)

Page 92: Linux 操作系统

92

变量 在 shell里,使用变量之前不需要事先对它做出声明。变量是在第一次用到的时候被创建的。在默认情况下,所有变量的值被认为是字符串。需要用工具程序将“数值”型字符串转换为正确的数值并操作。 shell 的变量名是大小写敏感的。在变量名前加上一个“ $”字符可以获得变量的内容。

Page 93: Linux 操作系统

93

变量 $ salutation=Hello $ echo $salutation Hello $ salutation=“Yes Dear” $ echo $salutation Yes Dear $ salutation=7+5 $ echo $salutation 7+5

Page 94: Linux 操作系统

94

引号的用法 一般情况下,参数之间是用空白字符分隔的,如

一个空格、制表符或换行符等。如想在一个参数里包含一个或多个这样的空白字符,就需要给参数加上引号。带有“$”字符的变量表达式放在双引号里,表达式会替换为它的值。如放在单引号里,则不替换。在“ $”字符前面加一个“ \”字符取消它的特殊意义。

Page 95: Linux 操作系统

95

引号的用法#!/bin/sh

myvar=“Hi there”

echo $myvarecho “$myvar”echo ‘$myvar’echo \$myvar

Page 96: Linux 操作系统

96

引号的用法echo Enter some textread myvar

echo ‘$myvar’ now equals $myvarexit 0

Page 97: Linux 操作系统

97

引号的用法程序输出结果如下:

Hi thereHi there$myvar$myvarEnter some textHello World$myvar mow equals Hello World

Page 98: Linux 操作系统

98

环境变量 环境变量是 shell预先初始化的一些变量,环境

变量可被子进程继承。环境变量通常使用大写字母作名字。具体有哪些环境变量取决于个人配置。

$HOME 当前用户的登陆子目录 $PATH 以冒号分隔的用来搜索命令的子目录清单 $PS1 命令行提示符 $PS2 辅助提示符,用来提示后续输入

Page 99: Linux 操作系统

99

环境变量 $IFS 输入区的分隔符。 shell读取输入数据时会

将一组字符视为单词之间的分隔字符,他们通常是空格、制表符和换行符

$0 shell脚本程序的名字 $# 传递到脚本程序的参数个数 $$ 该 shell脚本程序的进程 ID

Page 100: Linux 操作系统

100

参数变量 shell脚本程序在调用时还带有参数,会产生参

数变量。 $1,$2,… 脚本程序的第一个参数、第二个参数、…

$* 全体参数组成的清单,各参数之间用环境变量 IFS中的第一个字符分隔开

$@ 全体参数组成的清单,不使用 IFS 分隔符

Page 101: Linux 操作系统

101

$*与 $@的区别$ IFS=‘’$ set foo bar bam$ echo “$@”foo bar bam$ echo “$*”foobarbam$ unset IFS$ echo “$*”foo bar bam

Page 102: Linux 操作系统

102

参数和环境变量的例子#!/bin/sh

salutation=“hello”echo $salutationecho “The program $0 is now running”echo “The second parameter was $2”echo “The first parameter was $1”echo “The parameter list was $*”echo “The user’s home directory is $HOME”

Page 103: Linux 操作系统

103

参数和环境变量的例子echo “Please enter a new greeting”read salutation

echo $salutationecho “The script is now complete”exit 0

Page 104: Linux 操作系统

104

参数和环境变量的例子输出$ ./try_variables foo bar bazHelloThe program ./try_variables is now runningThe second parameter was barThe first parameter was fooThe parameter list was foo bar bazThe user’s home directory is /home/rickPlease enter a new greetingSireSireThe script is now complete$

Page 105: Linux 操作系统

105

条件测试 shell 的条件测试命令可以对命令的退出码、字

符串比较、算术比较、文件属性进行测试。 shell的条件测试命令(布尔判断命令)有 test和 []。

例如,检查一个文件是否存在代码如下: if test –f fred.c then … fi

Page 106: Linux 操作系统

106

条件测试 或者 : if [ -f fred.c ] then … fi

Page 107: Linux 操作系统

107

条件测试 - 字符串比较string1 = string2 如果两个字符串相同则结果 为真string1 != string2 如果两个字符串不同则结果 为真-n string 如果字符串不是空则结果为 真-z string 如果字符串是空则结果为真

Page 108: Linux 操作系统

108

条件测试 - 算术比较expression1 –eq expression2 如果两个表达式相等则结果为真expression1 –ne expression2 如果两个表达式不等则结果为真expression1 –gt expression2 如果前一个表达式大于后一个表达式则结果为真expression1 –ge expression2 如果前一个表达式大于或等于后一个表达式则结果为真

Page 109: Linux 操作系统

109

条件测试 - 算术比较expression1 –lt expression2 如果前一个表达式小于后一个表达式则结果为真expression1 –le expression2 如果前一个表达式小于或等于后一个表达式则结果为真!expression1 如果表达式为假则结果为真, 表达式结果为真则结果为假

Page 110: Linux 操作系统

110

条件测试 - 文件测试-d file 如果文件是一个子目录则结果为真-e file 如果文件存在则结果为真-f file 如果文件是一个普通文件则结果为真-g file 如果文件的 set-group-id位被设置则结果 为真

Page 111: Linux 操作系统

111

条件测试 - 文件测试-r file 如果文件可读则结果为真-s file 如果文件长度不为 0则结果为真-u file 如果文件的 set-user-id位被设置则结果 为真-w file 如果文件可写则结果为真-x file 如果文件可执行则结果为真

Page 112: Linux 操作系统

112

控制结构 -if 语句if conditionthen statementselse statementsfi

Page 113: Linux 操作系统

113

控制结构 -if 语句例子#!/bin/sh

echo “Is it morning? Please answer yes or no”

read timeofday

if [ $timeofday = “yes” ]; then

echo “Good morning”

Page 114: Linux 操作系统

114

控制结构 -if 语句例子else

echo “Good afternoon”

fi

exit 0

如果直接输入回车键会怎样?

Page 115: Linux 操作系统

115

控制结构 -elif 语句#!/bin/sh

echo “Is it morning? Please answer yes or no”read timeofday

if [ “$timeofday” = “yes” ]; then echo “Good morning”

Page 116: Linux 操作系统

116

控制结构 -elif 语句elif [ “$timeofday” = “no” ]; then echo “Good afternoon”else echo “Sorry, $timeofday not recognized . Enter yes or no”

exit 1fi

exit 0

Page 117: Linux 操作系统

117

控制结构 -for 语句for variable in valuesdo statementsdone

Page 118: Linux 操作系统

118

控制结构 -for 语句例子:for foo in bar fud 43do echo $foodoneexit 0

Page 119: Linux 操作系统

119

控制结构 -for 语句结果:barfud43

Page 120: Linux 操作系统

120

控制结构 -for 语句例子:

#!/bin/sh

for file in $(ls f*.sh)do lpr $filedoneexit 0

Page 121: Linux 操作系统

121

控制结构 -while 语句while conditiondo statementsdone

Page 122: Linux 操作系统

122

控制结构 -while 语句例子:口令字检查程序#!/bin/sh

echo “enter password”read trythis

while [ “$trythis” != “secret” ];do echo “Sorry, try again” read trythisdoneexit 0

Page 123: Linux 操作系统

123

控制结构 -while 语句例子:命令执行特定的次数

#!/bin/sh

foo=1

while [ “$foo” –le 20 ];do echo “we go again” foo=$(($foo+1))doneexit 0

Page 124: Linux 操作系统

124

控制结构 -until 语句until conditiondo statementsdone

Page 125: Linux 操作系统

125

控制结构 -until 语句例子:报警程序#!/bin/sh

until who | grep “$1” > /dev/nulldo sleep 60doneecho –e \\aecho “***** $1 has just logged in *****”exit 0

Page 126: Linux 操作系统

126

控制结构 -case 语句case variable in pattern [ | pattern ]… ) statements;; pattern [ | pattern ]… ) statements;;esac

Page 127: Linux 操作系统

127

控制结构 -case 语句例子:#!/bin/sh

echo “Is it morning? Please answer yes or no”read timeofday

case “$timeofday” yes) echo “Good Morning”;; no ) echo “Good Afternoon”;; y ) echo “Good Morning”;; n ) echo “Good Afternoon”;; * ) echo “Sorry, answer not recognized”;;esacexit 0

Page 128: Linux 操作系统

128

控制结构 -case 语句例子:#!/bin/sh

echo “Is it morning? Please answer yes or no”read timeofday

case “$timeofday” yes | y | Yes | YES) echo “Good Morning”;; n* | N* ) echo “Good Afternoon”;; * ) echo “Sorry, answer not recognized”;;esac

exit 0

Page 129: Linux 操作系统

129

控制结构 -case 语句例子:#!/bin/sh

echo “Is it morning? Please answer yes or no”read timeofday

case “$timeofday” yes | y | Yes | YES) echo “Good Morning” echo “Up bright and early this morning” ;;

Page 130: Linux 操作系统

130

控制结构 -case 语句 [nN] ) echo “Good Afternoon” ;; * ) echo “Sorry, answer not recognized” echo “Please answer yes or no” exit 1 ;;esac

exit 0

Page 131: Linux 操作系统

131

AND 命令表 AND命令表结构允许我们按这样的方式执行一连串命令:只有前面的所有命令都执行成功的情况下才执行后一条命令。

语法: statement1 && statement2 && statement3 && …

Page 132: Linux 操作系统

132

AND 命令表 例子:

#!/bin/sh

touch file_one rm –f file_two

Page 133: Linux 操作系统

133

AND 命令表 if [ -f file_one ] && echo “hello” && [ -f file_two ] && echo “there” then echo “in if” else echo “in else” fi

exit o

Page 134: Linux 操作系统

134

OR 命令表 OR命令表允许我们持续执行一系列命令直到有

一条成功为止,其后的命令将不再被执行。 语法: statement1 || statement2 || statement3 || …

Page 135: Linux 操作系统

135

OR 命令表 例子:

#!/bin/sh

rm –f file_one

if [ -f file_one ] || echo “hello” || echo “there” then echo “in if”

Page 136: Linux 操作系统

136

OR 命令表 else echo “in else” fi

exit 0

Page 137: Linux 操作系统

137

语句块 在只允许使用单个语句的地方使用多条语句,可

以把它们括在花括号 {}里来构造一个语句块。

Page 138: Linux 操作系统

138

语句块 例子: get_confirm && { grep –v “$cacatnum” $tracks_file > $temp_file cat $temp_file > $tracks_file echo add_record_tracks }

Page 139: Linux 操作系统

139

函数语法:function_name(){ statements}

Page 140: Linux 操作系统

140

函数例子:#!/bin/sh

foo(){ echo “Function foo is executing”}

echo “script starting”fooecho “script ended”exit 0

Page 141: Linux 操作系统

141

函数 -局部变量#!/bin/sh

sample_text=“globle varable”

foo(){ local sample_text=“local variable” echo “Function foo is executing” echo $sample_text}

Page 142: Linux 操作系统

142

函数 -局部变量echo “script starting”echo $sample_text

foo

echo “script ended”echo $sample_text

exit 0

Page 143: Linux 操作系统

143

命令 -break

break命令用于从 for、while 或 until循环里中途退出例子:#!/bin/sh

rm –rf fred*echo > fred1echo > fred2mkdir fred3echo > fred4

Page 144: Linux 操作系统

144

命令 -break

for file in fred*do if [ -d “$file” ]; then break;fidoneecho first directory starting fred was $filerm –rf fred*exit 0

Page 145: Linux 操作系统

145

命令 -continue

continue 命令使 for、while 或 until循环跳到下一个循环继续执行,循环变量取循环清单里的下一个值。

例子: #!/bin/sh

rm –rf fred* echo > fred1 echo > fred2 mkdir fred3 echo > fred4

Page 146: Linux 操作系统

146

命令 -continue

for file in fred* do if [ -d “$file” ]; then echo “skipping directory $file” continue; fi done rm –rf fred* exit 0

Page 147: Linux 操作系统

147

命令 - : 冒号命令是一个空命令。偶尔会被用来简化逻辑条件,相当于 true 的一个假名。

Page 148: Linux 操作系统

148

命令 - : 例子: #!/bin/sh

rm –f fred if [ -f fred ]; then : else echo file fred did not exist fi

exit 0

Page 149: Linux 操作系统

149

命令 - .

在一般情况下, shell 在执行外部命令和脚本程序的时候,会创建一个新的环境(子 shell)。子环境执行完毕后被丢弃,只有退出码返回给上一级 shell 。 .命令和外部命令 source 在当前 shell 中执行脚本中的命令,这样脚本中命令对环境变量的修改可以保存下来。

Page 150: Linux 操作系统

150

命令 - .

脚本 classic_set为老开发工具设置环境 #!/bin/sh version=classic PATH=/usr/local/old_bin:/usr/bin:/bin PS1=“classic>” 脚本 latest_set为新开发工具设置环境 #!/bin/sh version=latest PATH=/usr/local/new_bin:/usr/bin:/bin PS1=“latest>”

Page 151: Linux 操作系统

151

命令 - . 执行结果$ . ./classic_setclassic> echo $versionclassicclassic>. ./latest_setlatest> echo $versionlatestlatest>

Page 152: Linux 操作系统

152

命令 - eval

eval 命令对参数进行求值操作。例子:

foo=10x=fooy=‘$’$xecho $y

输出是 $foo

Page 153: Linux 操作系统

153

命令 - eval

foo=10x=fooeval y=‘$’$xecho $y

输出是 10

Page 154: Linux 操作系统

154

命令 - exec

exec命令被用来以另一个不同程序替换掉当前的 shell 。例子:

exec wall “Thank you for all the fish”

这个命令会用wall 替换掉当前的 shell ,脚本中后面的程序就不会执行了。

Page 155: Linux 操作系统

155

命令 - exit n

exit n命令使脚本程序以退出码n结束运行。 0 脚本执行成功 1-125 脚本程序用出错码 126 文件是不可执行的 127 命令未找到 128以上 引发一个出错信号

Page 156: Linux 操作系统

156

命令 - export

export命令用于创建环境变量,并被子 shell继承。 语法: export name=word 脚本 export2: #!/bin/sh echo “$foo” echo “$bar”

Page 157: Linux 操作系统

157

命令 - export

脚本 export1: #!/bin/sh foo=“This is foo” export bar=“This is bar” export2

脚本 export1 运行结果: This is bar

Page 158: Linux 操作系统

158

命令 - expr

expr命令把它的参数当作一个算术表达式进行求值。 例子: x=`expr $x + 1` 注意: `为反引号, +号两边需要空格

比较新的用法是 $((…))

Page 159: Linux 操作系统

159

命令 - expr

expr1 | expr2 如果 expr1非零则等于 expr1,否则等 于 expr2expr1 & expr2 如果两个表达式都是零则等于零,否 则等于 expr1expr1 = expr2 相等expr1 > expr2 大于expr1 >= expr2 大于等于expr1 < expr2 小于expr1 <= expr2 小于等于

Page 160: Linux 操作系统

160

命令 - expr

expr1 != expr2 不等于expr1 + expr2 加法expr1 - expr2 减法expr1 * expr2 乘法expr1 / expr2 整数除法expr1 % expr2 求整数除法的余数

Page 161: Linux 操作系统

161

命令 - set

set命令的作用是为 shell 设定参数变量。

例子:在 shell脚本里使用当前月份的名字 #!/bin/sh echo the date is $(date) set $(date) echo the month is $2

exit 0

Page 162: Linux 操作系统

162

命令 - shift

shift命令使所有参数变量向前移动一个位置, $2成为 $1, $3成为 $2,…。在扫描脚本程序参数时,对第 10个和以后参数需用 shift处理。

Page 163: Linux 操作系统

163

命令 - shift

例子: #!/bin/sh while [ “$1” != “” ]; do echo “$1” shift done

exit 0

Page 164: Linux 操作系统

164

命令的执行 我们希望执行一条命令并将命令的输出放到一

个变量里。注意:是命令的输出而不是返回值。 语法: $(command)

Page 165: Linux 操作系统

165

命令的执行 例子: #!/bin/sh echo The current directory is $PWD echo The current users are $(who) exit 0

Page 166: Linux 操作系统

166

命令的执行 - 算术扩展 完成简单的算术计算。 语法: $((expr1))

Page 167: Linux 操作系统

167

命令的执行 - 算术扩展 例子: #!/bin/sh x=0 while [ “$x” -ne 10 ]; do echo $x x=$(($x + 1)) done exit 0

Page 168: Linux 操作系统

168

命令的执行 -参数扩展 语法: ${variable}

例子:处理 1_tmp和 2_tmp文件(不能正常执行的情况)

#!/bin/sh for i in 1 2 do grep POSIX $i_tmp done

exit 0

Page 169: Linux 操作系统

169

命令的执行 -参数扩展 语法: ${variable}

例子:处理 1_tmp和 2_tmp文件(正常执行的情况) #!/bin/sh for i in 1 2 do grep POSIX ${i}_tmp done

exit 0

Page 170: Linux 操作系统

170

第四章 Linux 文件系统 传统 UNIX的文件系统为 s5文件系统, linux

的文件系统为 ext2或 ext3文件系统。 ext3文件系统与 ext2文件系统的不同之处在于 ext3文件系统使用了一个特殊的索引节点( inode)作为日志文件,除此之外, ext3与 ext2在格式上兼容。

Page 171: Linux 操作系统

171

第四章 Linux 文件系统 s5文件系统磁盘布局如下:

引导块 超级块 索引节点表 数据块 数据块 数据块 …

ext2文件系统磁盘布局如下(块大小为 1k) :引导块 超级块 组描述符表 组内块位视图 数据块组内索引节点位视图

组内磁盘布局

索引节点表

Page 172: Linux 操作系统

172

ext2 文件系统• ext2文件系统的块大小是一样的( 1024字节或 4096字节)

• 超级块的大小为 1024字节,单独占据一块• 组描述符表占据一个完整块( 1024字节或 4096字节)• 块位视图占据一个完整块( 1024字节或 4096字节)• 索引节点位视图占据一个完整块( 1024字节或 4096字节)

Page 173: Linux 操作系统

173

ext2 文件系统• 每组包含的块数是一样的• 每组包含的索引节点数是一样的• 块号从 0开始计数,为全局性的• 索引节点号从 1开始,为全局性的• 根目录的索引节点号为 2

Page 174: Linux 操作系统

174

ext2 文件系统 -超级块• Magic number ( 0xef53 )• inodes 计数 • blocks 计数 • 保留的 blocks 计数 • 空闲的 blocks 计数 • 空闲的 inodes 计数

Page 175: Linux 操作系统

175

ext2 文件系统 -超级块• 第一个数据 block • block 的大小 • 每 block group 的 block 数量• 每 block group 的 inode 数量 • 日志文件的 inode 号数 • 日志文件的设备号

Page 176: Linux 操作系统

176

ext2 文件系统 - 组描述符表struct ext3_group_desc{__u32 bg_block_bitmap; /* block 指针指向 block bitmap */__u32 bg_inode_bitmap; /* block 指针指向 inode bitmap */__u32 bg_inode_table; /* block 指针指向 inodes table */__u16 bg_free_blocks_count; /* 空闲的 blocks 计数 */__u16 bg_free_inodes_count; /* 空闲的 inodes 计数 */__u16 bg_used_dirs_count; /* 目录计数 */__u16 bg_pad; /* 可以忽略 */__u32 bg_reserved[3]; /* 可以忽略 */

};

Page 177: Linux 操作系统

177

ext2 文件系统 -索引节点struct ext3_inode {__u16 i_mode; /* File mode */__u16 i_uid; /* Low 16 bits of Owner Uid */__u32 i_size; /* 文件大小,单位是 byte */__u32 i_atime; /* Access time */__u32 i_ctime; /* Create time */__u32 i_mtime; /* Modificate time */__u32 i_dtime; /* Delete Time */__u16 i_gid; /* Low 16 bits of Group Id */__u16 i_links_count; /* Links count */__u32 i_blocks; /* blocks 计数 */__u32 i_flags; /* File flags */

Page 178: Linux 操作系统

178

ext2 文件系统 -索引节点__u32 l_i_reserved1; /* 可以忽略 */__u32 i_block[EXT3_N_BLOCKS]; /* 一组 block 指针 */__u32 i_generate; /* 可以忽略 */__u32 i_file_acl; /* 可以忽略 */__u32 i_dir_acl; /* 可以忽略 */__u32 i_faddr; /* 可以忽略 */__u8 l_i_frag; /* 可以忽略 */__u8 l_i_fsize; /* 可以忽略 */__u16 i_pad1; /* 可以忽略 */__u16 l_i_uid_high; /* 可以忽略 */__u16 l_i_gid_high; /* 可以忽略 */__u32 l_i_reserved2; /* 可以忽略 */

};

Page 179: Linux 操作系统

179

ext2 文件系统 -索引节点 在 inode 里面存放 EXT3_N_BLOCKS( = 15)这么

多个 block 指针。用户数据就从这些 block 里面获得。这组 15 个 block 指针的前 12 个是 direct blocks ,里面直接存放的就是用户数据。第 13 个 block是 indirect block,里面存放的全部是 block 指针,这些 block 指针指向的 block 才被用来存放用户数据。第 14 个 block 是 double indirect block,里面存放的全是 block 指针,这些 block 指针指向的 block 也被全部用来存放 block 指针,而这些 block 指针指向的 block,才被用来存放用户数据。第 15 个 block 是 triple indirect block。

Page 180: Linux 操作系统

180

ext2 文件系统 - 目录文件struct ext3_dir_entry_2 {__u32 inode; /* Inode 号数 */__u16 rec_len; /* Directory entry length */__u8 name_len; /* Name length */__u8 file_type;char name[EXT3_NAME_LEN]; /* File name */};

Page 181: Linux 操作系统

181

与文件操作有关的系统调用• int open(const char *path, int oflags); int open(const char *path,int oflags, mode_t mode);• size_t read(int fildes, const void *buf, size_t nbyte

s);• size_t write(int fildes, const void *buf, size_t nbyte

s);• int close(int fildes);• int ioctl(int fildes, int cmd,…);

Page 182: Linux 操作系统

182

与文件操作有关的系统调用• off_t lseek(int fildes, off_t offset, int whence);• int fstat(int fildes, struct stat *buf);• int stat(const char* path, stuct stat *buf);• int lstat(const char *path, struct stat *buf);• int dup(int fildes);• int dup2(int fildes, int fildes2);

Page 183: Linux 操作系统

183

与文件操作有关的系统调用• int chmod(const char *path, mode_t mode);• int chown(const char *path, uid_t owner, gid_t group);

• int unlink(const char *path1, const char *path2);• int link(const char *path1, const char *path2);• int symlink(const char *path1, const char *path2);

Page 184: Linux 操作系统

184

与文件操作有关的系统调用• int mkdir(const char *path, mode_t mode);• int rmdir(const char *path);• int chdir(const char * path);• int fcntl(int fildes, int cmd);• int fcntl(int fildes, int cmd, long arg);

Page 185: Linux 操作系统

185

系统调用 -open

#include <fcntl.h>#include <sys/type.h>#include <sys/stat.h>

int open(const char *path, int oflags);int open(const char *path,int oflags, mode_t mode);

Page 186: Linux 操作系统

186

系统调用 -open

path 为路径名

oflags 定义对打开的文件进行的操作,为访问模式与可选模式的组合 访问模式: O_RDONLY 以只读方式打开 O_WRONLY 以只写方式打开 O_RDWR 以读写方式打开 可选模式: O_APPEND 将写入的数据追加到文件尾 O_TRUNC 将文件清空,丢弃现有文件内容 O_CREAT 按mode 中给出的访问模式创建文件

Page 187: Linux 操作系统

187

系统调用 -open

mode 当 oflags里有 O_CREAT时需要mode参数,mode 是以下可

选参数的组合 S_IRUSR 文件所有者读权限 S_IWUSR 文件所有者写权限 S_IXUSR 文件所有者执行权限 S_IRGRP 组用户读权限 S_IWGRP 组用户写权限 S_IXGRP 组用户执行权限 S_IROTH 其他用户读权限 S_IWOTH 其他用户读权限 S_IXOTH 其他用户读权限

Page 188: Linux 操作系统

188

系统调用 -open

unmask 系统变量为 3个八进制数,分别对应所有者、同组用

户、其他用户,如对应位置位则表示禁止相应的权限。0-不禁止任何权限、 1- 禁止执行权限、 2- 禁止写权限、 4-禁

止读权限

返回值 如果成功返回文件描述符,失败返回 -1,全局变量 errno保存出错码

Page 189: Linux 操作系统

189

系统调用 -read

#include <unistd.h>

size_t read(int fildes, const void *buf, size_t nbytes);

fildes 为文件描述符buf 为缓冲区指针nbytes 为要读的字节数

返回值 为实际读的字节数、 -1为错误

Page 190: Linux 操作系统

190

系统调用 -read

#include <unistd.h>#include <stdlib.h>

int main(){char buffer[128];int nred;

nred=read(0,buffer,128);

Page 191: Linux 操作系统

191

系统调用 -read

if(nred==-1)write(2,”A read error has ocurred\n”,26);

if((write(1,buffer,nred))!=nred) write(2,”A write error has ocurred\n”,27);

exit(0);}

Page 192: Linux 操作系统

192

系统调用 -write

#include <unistd.h>

size_t write(int fildes, const void *buf, size_t nbytes);

fildes 为文件描述符buf 为缓冲区指针nbytes 为要写的字节数

返回值 为实际写的字节数、 -1为错误

Page 193: Linux 操作系统

193

系统调用 -close

#include <unistd.h>

int close(int fildes);

close 中止文件描述符 fildes 与文件之间的关联

返回值 0-成功、 -1-错误

Page 194: Linux 操作系统

194

系统调用 -lseek

#include <unistd.h>#include <sys/types.h>

off_t lseek(int fildes, off_t offset, int whence);

Lseek对文件描述符 fildes 的读写指针进行设置,用来设置文件的下一个读写位置。即可以把指针设置到绝对位置,也可以设置到相对于当前位置或文件尾的某个相对位置。

Page 195: Linux 操作系统

195

系统调用 -lseek

offset 偏移量

whence 可以取下列值: SEEK_SET offset是一个绝对值 SEEK_CUR offset是从当前位置算起的一个相对位置 SEEK_END offset是从文件尾算起的一个相对位置

返回值 文件头到新指针位置的偏移量、 -1- 失败

Page 196: Linux 操作系统

196

open,read,write,lseek 用法#include <unistd.h>#include <stdlib.h>#include <sys/types.h>

/****************进程 A*******************/main(){int fd;char buf[512];

fd=open(“/etc/passwd”, S_IRUSR);

read(fd, buf, sizeof(buf));read(fd, buf, sizeof(buf));

}

Page 197: Linux 操作系统

197

open,read,write,lseek 用法/****************进程 B*******************/main(){int fd, i;char buf[512];

for(i=0; i<sizeof(buf); i++) buf[i]=‘a’;

fd=open(“/etc/passwd”, S_IWUSR);

write(fd, buf, sizeof(buf));write(fd, buf, sizeof(buf));

}

Page 198: Linux 操作系统

198

open,read,write,lseek 用法#include <unistd.h>#include <stdlib.h>#include <fcntl.h>#include <sys/types.h>

main(int argc, char *argv[]){int fd, skval;char c;

if(argc!=2)exit();

fd=open(argv[1], S_IRUSR);

Page 199: Linux 操作系统

199

open,read,write,lseek 用法

if(fd == -1)exit();

while((skval=read(fd, &c, 1))==1){

printf(“char %c\n”,c);skval=lseek(fd, 1023, SEEK_SET);printf(“new seek val %d\n”,skval);

}}

Page 200: Linux 操作系统

200

系统调用 -fstat

#include <unistd.h>#include <sys/stat.h>#include <sys/types.h>

int fstat(int fildes, struct stat *buf);int stat(const char *path, struct stat *buf)int lstat(const char *path, struct stat *buf)

Page 201: Linux 操作系统

201

系统调用 -fstat

fstat系统调用返回文件的状态信息。

st_mode 文件权限和文件类型信息st_ino 文件的索引节点st_dev 保存文件的设备st_uid 文件的所有者st_gid 文件的组所有者st_atime 文件上次被访问时间st_ctime 文件上次被修改时间(文件权限、所有者、内容 等)st_mtime 文件内容方面上次被修改的时间st_nlink 文件的链接计数

Page 202: Linux 操作系统

202

系统调用 -fstat

struct stat { dev_t st_dev; /* 设备 */ ino_t st_ino; /* 节点 */ mode_t st_mode; /* 模式 */ nlink_t st_nlink; /* 硬连接 */ uid_t st_uid; /* 用户 ID */ gid_t st_gid; /* 组 ID */ dev_t st_rdev; /* 设备类型 */ off_t st_off; /* 文件字节数 */ unsigned long st_blksize; /* 块大小 */ unsigned long st_blocks; /* 块数 */ time_t st_atime; /* 最后一次访问时间 */ time_t st_mtime; /* 最后一次修改时间 */ time_t st_ctime; /* 最后一次改变时间 (指属性 ) */

};

Page 203: Linux 操作系统

203

系统调用 -dup,dup2

#include <unistd.h>

int dup(int fildes);int dup2(int fildes, int fildes2)

dup系统调用将一个文件描述符拷贝到该用户文件描述符表中的第一个空槽中返回一个新的文件描述符。 dup2系统调

用将文件描述符 fildes 复制给 fildes2。

Page 204: Linux 操作系统

204

系统调用 -dup,dup2

#include <unistd.h>

main(int argc, char *argv[]){int fd,i;char buf[512];

if(argc!=2)exit();

fd=open(argv[1], S_IWUSR);

if(fd == -1)exit();

Page 205: Linux 操作系统

205

系统调用 -dup,dup2

close(1);dup(fd);

for(i=0; i<sizeof(buf); i++) buf[i]=‘a’;

write(1, buf, sizeof(buf));}

Page 206: Linux 操作系统

206

系统调用 -pipe

#include<unistd.h>

int pipe(int fildes[2]);

pipe调用可以创建一个管道 (通信缓冲区 ).当调用成功时 ,我

们可以访问文件描述符 fildes[0],fildes[1].其中 fildes[0]是用来读的文件描述符 ,而 fildes[1]是用来写的文件描述符 .

Page 207: Linux 操作系统

207

系统调用 -pipe

#include <stdio.h> #include <stdlib.h>#include <unistd.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/wait.h> #define BUFFER 255

Page 208: Linux 操作系统

208

系统调用 -pipe

int main(int argc,char **argv) { char buffer[BUFFER+1]; int fd[2];

if(argc!=2) { fprintf(stderr,"Usage:%s string\n\a",argv[0]); exit(1); }

Page 209: Linux 操作系统

209

系统调用 -pipe

if(pipe(fd)!=0) { fprintf(stderr,"Pipe Error:%s\n\a",strerror(errno)); exit(1); }

if(fork()==0) { close(fd[0]); printf("Child[%d] Write to pipe\n\a",getpid()); snprintf(buffer,BUFFER,"%s",argv[1]); write(fd[1],buffer,strlen(buffer)); printf("Child[%d] Quit\n\a",getpid()); exit(0); }

Page 210: Linux 操作系统

210

系统调用 -pipe

else { close(fd[1]); printf("Parent[%d] Read from pipe\n\a",getpid()); memset(buffer,'''''''',BUFFER+1); read(fd[0],buffer,BUFFER); printf("Parent[%d] Read:%s\n",getpid(),buffer); exit(1); }

}

Page 211: Linux 操作系统

211

系统调用 -chmod

#include <sys/stat.h>

int chmod(const char *path, mode_t mode);

chmod系统调用修改路径名 path 文件的访问权限,mode 设置

类似于 open中的mode 。

Page 212: Linux 操作系统

212

系统调用 -unlink,link,symlink

#include <unistd.h>

int unlink(const char *path);int link(const char *path1, const char *path2);int symlink(const char *path1, const char *path2);

link和 unlink用来创建和删除文件的链接, symlink用来创

建 path1的符号链接。

Page 213: Linux 操作系统

213

系统调用 -mkdir,rmdir

#include <sys/stat.h>

int mkdir(const char *path, mode_t mode);int rmdir(const char *path);

mkdir和 rmdir用来创建和删除目录,

Page 214: Linux 操作系统

214

系统调用 -chdir

#include <unistd.h>

int chdir(const char *path);

chdir用来改变当前目录,

Page 215: Linux 操作系统

215

系统调用 -fcntl

#include <fcntl.h>

int fcntl(int fildes, int cmd);int fcntl(int fildes, int cmd, long arg);

cmd=F_DUPFD 复制描述符,复制文件描述符 filedes ,新文

件描述符作为函数值返回。它是尚未打开的 各描述符中大于或等于第三个参数值 (取为整 型值 )中各值的最小值。新描述符有它自己的 一套文件描述符标志,其 FD_CLOEXEC文件描 述符标志则被清除这表示该描述符在 exec时 仍保持开放。

Page 216: Linux 操作系统

216

系统调用 -fcntl

cmd=F_GETFD 获取文件描述符标志 , 唯一标志 : FD_CLOEXEC。对应于 filedes 的文件描述符标 志作为函数值返回。

cmd=F_SETFD 对于 filedes 设置文件描述符标志。新标志值

是按第 3个参数 (取为整型值 ) 设置的 0-在 exec时不关闭 1-在 exec时关闭。

cmd=F_GETFL 获取文件状态标志。对应于 filedes 的文件状

态标志作为函数值返回。在我们说明 open函 数时, 已说明了文件状态标志

Page 217: Linux 操作系统

217

系统调用 -fcntl

cmd=F_SETFL 设置文件状态标志 . 只能修改 : O_APPEND,

O_NONBLOCK和 O_ASYNC

cmd=F_GETLK, F_SETLK or F_SETLKW 获取 /设置记录锁

cmd=F_GETOWN 取当前接收SIGIO和 SIGURG信号的进程 ID或进

程组 ID

cmd=F_SETOWN 设置接收SIGIO和 SIGURG信号的进程ID或进程

组 ID。正的 arg 指定一个进程 ID。负的 arg表 示等于 arg 绝对值的一个进程组 ID。

Page 218: Linux 操作系统

218

系统调用 -mount,umount

mount 系统调用用来安装一个设备。

语法:mount(const char *dev, const char *dir, int arg);

umount卸载一个设备

语法: umount(const char *dev);

Page 219: Linux 操作系统

219

系统调用 -综合应用读目录的函数

struct dirent *readdir(int fildes);

struct dirent{

long d_ino; /* inode number */off_t d_off; /* offset to this dirent */unsigned short d_reclen; /* length of this d_name */char d_name[NAME_MAX+1]; /* file name */

}

Page 220: Linux 操作系统

220

系统调用 -综合应用例子:一个类似 cp -r 的程序

#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <dirent.h> #include <errno.h> #include <string.h>

#define RWBUFSIZ 0xffff

Page 221: Linux 操作系统

221

系统调用 -综合应用char rwbuf[RWBUFSIZ];

int init (int, char **);

long copy (char *, char *);

int main (int argc, char *argv[]) {

long count; if (!init (argc, argv)) { count = copy (argv[1], argv[2]); printf ("%ld files copied\n", count); } return 0;

}

Page 222: Linux 操作系统

222

系统调用 -综合应用int init (int argc, char *argv[]) {

switch (argc) { case (2): argv[2] = get_current_dir_name(); /*copy files to PWD */

case (1): printf (“Usage:copy [pathname] [pathname]\n”); return -1; } return 0;

}

Page 223: Linux 操作系统

223

系统调用 -综合应用void error (char *pth) {

switch (errno) { case (ENOENT): printf ("File or dirent %s dosen't exist\n", pth); break; case (EACCES): printf ("Acces error %s\n", pth); break; case (EEXIST): printf ("File or dirent %s already exist\n", pth); default: printf ("Can't open file or dirent %s\n", pth); }

}

Page 224: Linux 操作系统

224

系统调用 -综合应用void getfullname (char *get, char *path, char *name) {

char *p; strcpy (get, path); strcat (get, "/"); if ((p = strrchr (name, '/')) != NULL) strcat (get, p + 1); else strcat (get, name);

} mode_t mode(int umode) {

return (mode_t)(umode & 0777); }

Page 225: Linux 操作系统

225

系统调用 -综合应用long copy (char *pth, char *opth) {

int pthid, opthid, rn; long count = 0; struct stat statue; DIR *dp; struct dirent *dir; char fullname[NAME_MAX + 1], nextfile[NAME_MAX + 1];

if (stat (pth, &statue)) { errno = EACCES; error (pth); }

Page 226: Linux 操作系统

226

系统调用 -综合应用 else

{ if (S_ISDIR (statue.st_mode)) { getfullname (fullname, opth, pth); mkdir (fullname,mode(statue.st_mode));

dp = opendir (pth); while (dir = readdir (dp)) if ( strcmp(dir->d_name , ".") &&

strcmp(dir->d_name , "..")) { getfullname (nextfile, pth, dir->d_name); count += copy (nextfile, fullname); } closedir (dp); }

Page 227: Linux 操作系统

227

系统调用 -综合应用 else

{ if (!(pthid = open (pth, O_RDONLY))) error (pthid); else { getfullname (fullname, opth, pth); if ((opthid = creat (fullname,mode(statue.st_mode)))

== -1) { errno = EACCES; error (fullname); }

Page 228: Linux 操作系统

228

系统调用 -综合应用 else

{ while (rn = read (pthid, rwbuf, RWBUFSIZ)) write (opthid, rwbuf, rn); count++; close(opthid); } close(pthid); } } return count; }

}

Page 229: Linux 操作系统

229

有关进程的一些概念进程:地址空间 +系统资源。地址空间和系统资源被属于同 一进程的一个或者多个线程所使用。一个进程包括程 序代码、数据、堆栈、打开的文件和环境。

线程:是位于一个进程的地址空间内、可被单独调度运行的 单元。又称为轻量进程。

进程的父子关系、僵死子进程、进程的真正用户标识号、有效用户标识号、保存的用户标识号、 setuid 程序、信

号(软中断)。

Page 230: Linux 操作系统

230

父 进 程数据

打开的文件当前目录改变的根目录

核心栈

父进程

U区本进程区表

父 进 程用户栈

子 进 程数据

打开的文件当前目录改变的根目录

核心栈

子进程

U区本进程区表

子 进 程用户栈

共享正文

文件表

索引节点表

Page 231: Linux 操作系统

231

与进程相关的系统调用• pid_t fork(void);• int execl(const char *path, const char *arg0, …, (char *)0); int execlp(const char *file, const char *arg0, …, (char *)0); int execle(const char *path, const char *arg0, …, (char *)0,const char *envp[]); int execv(const char *path, const char *argv[]); int execvp(const char *file, const char *argv[]); int execve(const char *path, const char *argv[], const char *envp[]);

Page 232: Linux 操作系统

232

与进程相关的系统调用• pid_t wait(int *stat_loc);• pid_t waitpid(pid_t pid, int *stat_loc, int options);• void (*signal(int sig, void (*func)(int)))(int);• int kill(pid_t pid, int sig);• int pause(void);• unsigned int sleep(unsigned int seconds);• unsigned int alarm(unsigned int seconds);

Page 233: Linux 操作系统

233

与进程相关的系统调用• int setuid(uid_t uid);• int seteuid(uid_t euid);• uid_t getuid(void);• uid_t geteuid(void);• int setgid(gid_t gid);• int setegid(gid_t gid);• gid_t getgid(void);• gid_t getegid(void);• int setpgid(pid_t pid,pid_t pgid);• int setpgrp(void);

Page 234: Linux 操作系统

234

与进程相关的系统调用• pid_t getpgid( pid_t pid); • pid_t getpgrp(void);• int nice(int inc);• void exit(int status);

Page 235: Linux 操作系统

235

系统调用 -fork

#include <unistd.h>

int pid_t fork(void);

说明: fork()产生一个新的子进程,子进程继承父进程的数据与堆栈空 间,并继承父进程的用户代码、组代码、环境变量、已打开的文 件、工作目录和资源限制等。子进程不会继承父进程的文件锁和 未处理的信号。

返回值:如果 fork成功则在父进程中返回子进程的 pid,在子进程中返 回 0。如果失败则返回 -1,错误码保存于 errno中。

错误码: EAGAIN 内存不足 ENOMEM 内存不足,无法配置核心所需的数据空间。

Page 236: Linux 操作系统

236

系统调用 -fork

#include <unistd.h>#include <fcntl.h>

int fdrd, fdwt;char c;

main(int argc, char *argv[]){if(argc!=3)exit(1);if((fdrd=open(argv[1],O_RDONLY))==-1)exit(1) ;if((fdwt=creat(argv[2],0666))==-1)exit(1);fork();rdwrt();exit();

}

Page 237: Linux 操作系统

237

系统调用 -fork

rdwrt(){for(;;){

if(read(fdrd, &c, 1)!=1)return;write(fdwt, &c, 1);

}}

Page 238: Linux 操作系统

238

系统调用 -execve

#include <unistd.h>

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

说明: execve()用 path指定的文件替换当前进程的

内存映像, argv为传给main的参数 ,envp为新

的环境变量。 返回值:成功则不会返回。如果失败则返回 -1,错 误码保存于 errno中。

Page 239: Linux 操作系统

239

系统调用 -execve

错误码: EACCES 文件不可执行,文件存储权限不足。 ENOEXEC 无法判断欲执行文件的格式。 E2BIG 参数数组过大。 EFAULT 参数 path 所指的字符串地址超出可存取空 间范围。 ENAMETOOLONG 参数 path 所指的字符串太长。 ENOMEM 核心内存不足。 ENOTDIR 参数 path 字符串所包含的目录路径并非有 效目录。

Page 240: Linux 操作系统

240

系统调用 -execve

#include <unistd.h>

main(){char *argv[]={“ls”,”-al”,”/etc/passwd”,(char *)0};char *envp[]={“PATH=/bin”,0};execve(”/bin/ls”,argv,envp);

}

Page 241: Linux 操作系统

241

系统调用 -wait

#include <sys/types.h>#include <sys/wait.h>

int pid_t wait(int *status);

说明:wait()暂停目前进程的运行,直到有信号来或子进程

结束。如果在调用wait时子进程已经结束则wait 会立 即返回子进程的结束状态值。如果进程捕俘“子进程 死”信号,则调用用户的软中断信号处理程序。如果 进程忽略“子进程死”软中断信号,则wait释放僵死子 进程的进程表项,然后寻找其他的子进程。 返回值:成功返回子进程 PID。

Page 242: Linux 操作系统

242

系统调用 -wait

#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/wait.h>

main(){pid_t pid;int status, i;if(fork()==0){

printf(“This is the child process, pid=%d\n”, getpid());

exit(5);}

Page 243: Linux 操作系统

243

系统调用 -wait

else{sleep(1);printf(“This is the parent process, wait for

child …\n”);pid=wait(&status);i=WEXITSTATUS(satus);printf(“child’s pid=%d, exit satus = &d\n”, pid, i);

}}

Page 244: Linux 操作系统

244

系统调用 -wait

#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/wait.h>#include <signal.h>

main(int argc, char *argv[]){int ret_val,ret_code,i;

if(argc>1)signal(SIGCLD,SIG_IGN);

Page 245: Linux 操作系统

245

系统调用 -wait

for(i=0; i<15; i++){if(fork()==0){

printf(“child proc pid=%d\n”, getpid());exit(1);

}}ret_val=wait(&ret_code);printf(“wit ret_val%dret_code%d\n”,ret_val,ret_code);

}

Page 246: Linux 操作系统

246

系统调用 -waitpid

#include <sys/types.h>#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *stat_loc, int options);

说明:waitpid()暂停目前进程的运行,直到有信号来或子 进程结束。如果在调用wait时子进程已经结束则wait 会立即返回子进程的结束状态值。

Page 247: Linux 操作系统

247

系统调用 -waitpid

参数 pid为欲等待的子进程识别码 pid<-1等待 gid为 pid 绝对值的任何子进程 pid=-1等待任何子进程,相当于wait pid=0 等待 gid与目前进程 gid 相同的子进程 pid>0 等待任何识别码为 pid的子进程

option 可以为 0或下面的 OR组合 WNOHANG 如果没有任何已经结束的子进程则马上返回,不等待 WUNTRACED 如果子进程进入暂停执行情况则马上返回,但结束状 态不予理会 返回值:成功返回子进程 PID。如果失败则返回 -1,错误码 保存于 errno中。

Page 248: Linux 操作系统

248

系统调用 -waitpid

子进程的结束状态返回后存于 status ,下面的宏可判别结束情况:

WIFEXITED(status) 如果子进程正常结束则为非 0值WEXITSTATUS(status) 取得子进程由 exit返回的结束代码,一般会先

用 WIFEXITED来判断是否正常结束才能使用此宏WIFSIGNALED(status) 如果子进程是因为信号而结束则此宏值为真WTERMSIG(status) 取得子进程因信号而终止的信号代码,一般会先 用WIFSIGNALED来判断才能使用此宏WIFSTOPPED(status) 如果子进程处于暂停执行的情况则此宏为真WSTOPSIG(status) 取得引发子进程暂停的信号代码,一般会先用 WIFSTOOPED来判断后才使用此宏

Page 249: Linux 操作系统

249

系统调用 -signal

#include <signal.h>

void (*signal(int sig, void (*func)(int)))(int);

说明: signal 为指定的信号设置相应的信号处理函数,当指定的信号到 达时执行相应的信号处理函数如果 func不是函数指针,则必须是 下列两个常数之一: SIG_IGN 忽略相应的信号 SIG_DFL 将信号处理重置为缺省的处理方式

返回值:成功返回先前的信号处理函数指针。如果失败则返回 -1,错误 码 保存于 errno中。

注意事项:在接收到一次信号并处理后,系统自动将信号处理方式切换 回缺省的处理方式

Page 250: Linux 操作系统

250

系统调用 -signal

Page 251: Linux 操作系统

251

系统调用 -signal

Page 252: Linux 操作系统

252

系统调用 -signal

Page 253: Linux 操作系统

253

系统调用 -signal

Page 254: Linux 操作系统

254

系统调用 -kill

#include <unistd.h>

int kill(pid_t pid, int sig);

说明: kill 用来将信号 sig发给进程号为 pid的进程, pid有

以下几种情况: pid>0 将信号发给进程号为 pid的进程 pid=0 将信号发给同组的所有进程 pid=-1 将信号发给系统中所有进程 pid<0 将信号发给组号为 pid 绝对值的所有进程

Page 255: Linux 操作系统

255

系统调用 -kill

返回值:如果成功则返回 0。如果失败则返回 -1,错误码 保存于 errno中。

错误码: ENIVAL 参数 sig不合法 ESRCH 参数 pid所指的进程或进程组不存在 EPERM 权限不够无法将信号发给指定的进程

Page 256: Linux 操作系统

256

系统调用 -kill

#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/wait.h>#include <signal.h>

main(int argc, char *argv[]){pid_t pid;int status;

Page 257: Linux 操作系统

257

系统调用 -kill

if(!(pid=fork())){printf(“Hi I am child process!\n”);sleep(10);return;

}else{

printf(“send signal to child process (%d)\n”, pid);sleep(1);kill(pid,SIGABRT);wait(&status);if(WIFSIGNALED(status))

printf(“child process receive signal %d\n”, WTERMSIG(status));}

}

Page 258: Linux 操作系统

258

系统调用 -pause

#include <unistd.h>

int pause(void);

说明: pause暂停进程的执行,直到被信号唤醒 返回值:只返回 -1,错误码保存于 errno中。

错误码: EINTR 有信号到达中断了此函数

Page 259: Linux 操作系统

259

系统调用 -sleep

#include <unistd.h>

unsigned int sleep(unsigned int seconds);

说明: sleep暂停进程的执行 seconds 时间,或被信

号唤醒 返回值:若进程暂停到所指定时间则返回 0,若有信

号中断则返回剩余秒数。

Page 260: Linux 操作系统

260

系统调用 -alarm

#include <unistd.h>

unsigned int alarm(unsigned int seconds);

说明: alarm在过了 seconds 时间后给进程发信号

SIGALRM。如果 seconds 为 0,则之前设置的 闹钟被取消并返回剩余时间。 返回值:返回之前闹钟剩余的秒数,若之前未设闹 钟返回 0。

Page 261: Linux 操作系统

261

系统调用 -alarm

#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/wait.h>#include <signal.h>

void handler(){printf(“hello\n”);

}

Page 262: Linux 操作系统

262

系统调用 -alarm

main(int argc, char *argv[]){int i;

signal(SIGALRM, handler);alarm(5);for(i=1; i<7; i++){

printf(“sleep %d…\n”, i);sleep(1);

}}

Page 263: Linux 操作系统

263

系统调用 -setuid

#include <unistd.h>

int setuid(uid_t uid);

说明: setuid用来重新设置进程的真正用户标识号。此函数

只有在有效用户标识号为 0 ( root )的情况下才起作 用。当调用 setuid后 root权限会丧失,将不再能调用 setuid。如只是暂时放弃 root权限需用 seteuid。 返回值:执行成功返回 0。如果失败则返回 -1,错误码保

存 于 errno中。

Page 264: Linux 操作系统

264

系统调用 -seteuid

#include <unistd.h>

int seteuid(uid_t uid);

说明: seteuid用来重新设置进程的有效用户标识号。如果 进程的用户标识号为 root则将有效用户标识号设置为 uid,如果进程的用户标识号不是超级用户,而且 uid 的值是真正用户标识号或保存的用户标识号则将有效 用户标识号改为 uid。否则返回一个错误。

返回值:执行成功返回 0。如果失败则返回 -1,错误码保存

于 errno中。

Page 265: Linux 操作系统

265

系统调用 -seteuid

例子: 假定以下程序的所有者为maury(用户标识号为 8319),setuid位被置位,且所有用户都有权执行该文件。 假定用户mjb(用户标识号为 5088)和用户maury分

别拥有文件mjb和maury,且者两个文件只对他们的所有着具

有只读许可权。

Page 266: Linux 操作系统

266

系统系统调用 -seteuid

#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <sys/wait.h>#include <signal.h>

main(int argc, char *argv[]){int uid, euid, fdmjb, fdmaury;

uid=getuid();euid=geteuid();printf(“uid %d euid %d\n”, uid, euid);

Page 267: Linux 操作系统

267

系统系统调用 -seteuid

fdmjb=open(“mjb”,O_RDONLY);fdmaury=open(“maury”,O_RDONLY);printf(“fdmjb %d fdmaury %d\n”,fdmjb, fdmaury);

seteuid(uid);printf(“after steduid(%d):uid %d euid %d\n”, uid, getuid(),

geteuid());

fdmjb=open(“mjb”,O_RDONLY); fdmaury=open(“maury”,O_RDONLY); printf(“fdmjb %d fdmaury %d\n”,fdmjb, fdmaury);

seteuid(euid); printf(“after steduid(%d):uid %d euid %d\n”, uid, getuid(), geteuid());}

Page 268: Linux 操作系统

268

系统调用 -getuid

#include <unistd.h>#include <sys/types.h>

uid_t getuid(void);

说明: getuid用来取得进程的真正用户标识号。

返回值:真正用户标识号。

Page 269: Linux 操作系统

269

系统调用 -geteuid

#include <unistd.h>#include <sys/types.h>

uid_t geteuid(void);

说明: geteuid用来取得进程的有效用户标识号。

返回值:有效用户标识号。

Page 270: Linux 操作系统

270

系统调用 -setgid

#include <unistd.h>#include <sys/types.h>

int setgid(void);

说明: setgid用来设置进程的真正组标识号。如果 以超级用户身份调用则有效组标识号、真正 组标识号保存的组标识号都设成 gid.

返回值:设置成功返回 0,失败返回 -1。

Page 271: Linux 操作系统

271

系统调用 -setegid

#include <unistd.h>#include <sys/types.h>

int setegid(void);

说明: setegid用来设置进程的有效组标识号。

返回值:设置成功返回 0,失败返回 -1。

Page 272: Linux 操作系统

272

系统调用 -getgid

#include <unistd.h>#include <sys/types.h>

gid_t getgid(void);

说明: getuid用来取得进程的真正组标识号。

返回值:真正组标识号。

Page 273: Linux 操作系统

273

系统调用 -getegid

#include <unistd.h>#include <sys/types.h>

gid_t getegid(void);

说明: getegid用来取得进程的有效组标识号。

返回值:有效组标识号。

Page 274: Linux 操作系统

274

系统调用 -setpgid

#include <unistd.h>#include <sys/types.h>

int setpgid(pid_t pid,pid_t pgid);

说明: setpgid()将参数 pid 指定进程的进程组组 号设为参数 pgid 指定的组号。如果参数 pid 为 0,则设置当前进程的组号,如果参数 pgid 为 0,则会以当前进程的进程标识号来取代原 有的组号。

Page 275: Linux 操作系统

275

系统调用 -setpgid

返回值:执行成功则返回新的组号,如果有错误则 返回 -1,错误原因存于 errno中。

错误码: EINVAL 参数 pgid小于 0。 EPERM 进程权限不足,无法完成调用。 ESRCH 找不到符合参数 pid 指定的进程。

Page 276: Linux 操作系统

276

系统调用 -setpgrp

#include <unistd.h>#include <sys/types.h>

int setpgrp(void);

说明: setpgrp()将当前进程的组号设为当前进程的

进程号。此函数相当于调用相当于 setpgid(0,0)。

Page 277: Linux 操作系统

277

系统调用 -setpgrp

返回值:执行成功则返回新的组号,如果有错误则 返回 -1,错误原因存于 errno中。

Page 278: Linux 操作系统

278

系统调用 -getpgid

#include <unistd.h>#include <sys/types.h>

pid_t getpgid( pid_t pid);

说明: getpgid()用来取得参数 pid 指定进程的组 号。如果参数 pid为 0,则会取得当前进程的 组号。返回值:执行成功则返回组号,如果有错误则返回

-1,错误原因存于 errno中。

Page 279: Linux 操作系统

279

系统调用 -getpgrp

#include <unistd.h>#include <sys/types.h>

pid_t getpgrp(void);

说明: getpgrp()用来取得目前进程所属的组识别 码。此函数相当于调用 getpgid(0) ;返回值:执行成功则返回组号,如果有错误则返回 -1,错误原因存于 errno中。

Page 280: Linux 操作系统

280

系统调用 -getpid

#include <unistd.h>#include <sys/types.h>

pid_t getpid(void);

说明: getpid()用来取得当前进程的进程标识号。

返回值:当前进程的标识号。

Page 281: Linux 操作系统

281

系统调用 -getppid

#include <unistd.h>#include <sys/types.h>

pid_t getppid(void);

说明: getppid()用来取得当前进程的父进程标识 号。

返回值:当前进程的父进程标识号。

Page 282: Linux 操作系统

282

系统调用 -nice

#include <unistd.h>#include <sys/types.h>

int nice(int inc);

说明: nice()用来改变进程的进程的优先级。参数 inc数值越大则优先级越低。只有超级用户才 能使用负的 inc 值,提高进程的优先级。

Page 283: Linux 操作系统

283

系统调用 -exit

#include <unistd.h>#include <sys/types.h>

void exit(staus);

说明: exit用来正常终结目前进程的执行,并将参 数返回给父进程。

返回值:无。

Page 284: Linux 操作系统

284

linux IPC 概述 linux下的进程通信手段基本上是从unix平台上的进程

通信手段继承而来的。而对unix发展做出重大贡献的两大主力 AT&T的贝尔实验室及加州大学伯克利分校的伯克利软件发布中心( BSD)在进程间通信方面的侧重点有所不同。前者对unix早期的进程间通信手段进行了系统的改进和扩充,形成了“ system V IPC”,通信进程局限在单个计算机内;后者则跳过了该限制,形成了基于套接口( socket )的进程间通信机制。此外,由于 unix版本的多样性,电子电气工程协会( IEEE)开发了一个独立的 unix 标准,这个新的 ANSI unix 标准被称为计算机环境的可移植性操作系统界面(PSOIX )。

Page 285: Linux 操作系统

285

linux IPC 概述 最初 unix IPC包括:管道、 FIFO、信号; system V IPC包括: system V消息队列、 system V信号灯、 system V共享内存区; POSIX IPC包括: POSIX消息队列、POSIX 信号灯、 POSIX共享内存区。

Page 286: Linux 操作系统

286

linux IPC 概述 linux IPC 主要有:• 匿名管道(pipe)• 有名管道(named pipe)• 信号( signal)• 消息队列(message)• 信号量( semaphore)• 共享内存( shared memory )• 套接字( socket )

Page 287: Linux 操作系统

287

匿名管道( pipe) 匿名管道只可用于具有亲缘关系进程间的通信。

管道管道实现于内存中,缓冲区是有限的,一般为一个页面大小。管道的数据是字节流,应用程序之间必须事先确定特定的传输“协议”,采用传播具有特定意义的消息。管道是半双工的,数据只能向一个方向流动,需要双方通信时,需要建立起两个管道。

Page 288: Linux 操作系统

288

有名管道 以 FIFO的文件形式存在于文件系统中。有名管道的数据是字节流,应用程序之间必须事先确定特定的传输“协议”,采用传播具有特定意义的消息。有名管道是半双工的,数据只能向一个方向流动,需要双方通信时,需要建立起两个管道。

Page 289: Linux 操作系统

289

信号 信号用于通知异步事件发生,是一种软中断机制。除了用于通知进程系统事件外,还可以用于进程间通信,除此之外,进程还可以发送信号给进程本身。

由于历史上的原因, linux 信号分为不可靠信号和可靠信号两类。其中,不可靠信号对应于 system V信号,可靠信号对应于 BSD和 POSIX 信号。

Page 290: Linux 操作系统

290

信号 -信号响应机制 操作系统在进程每次从核心态返回到用户态时对信号进行响应。信号产生后到被响应之前处于 pending状态。

Page 291: Linux 操作系统

291

信号 - 不可靠信号 不可靠信号是指信号值小于 SIGRTMIN (一般情况下,SIGRTMIN=31, SIGRTMAX=63)的信号。不可靠信号主要表现在:

• 进程每次处理信号后,就将对信号的响应设置为默认动作,如不希望这样用户信号处理函数结尾再一次调用 signal()。

• 信号可能丢失。

注:在 linux中对不可靠信号机制做了改进:在调用完信号 处理函数后,不必重新安装该信号的处理函数。因此, linux下的不可靠信号问题主要指的是信号可能丢失。

Page 292: Linux 操作系统

292

信号 - 可靠信号 可靠信号是信号值位于 SIGRTMIN与 SIGRTMAX之间的信号,可靠信号不会丢失。

Page 293: Linux 操作系统

293

信号 -POSIX对信号的扩充 POSIX (继承BSD)除增加可靠信号外,还增

加了信号传递参数的能力和信号屏蔽(信号阻塞)的功能。信号屏蔽类似于中断屏蔽,进程可以在运行期间屏蔽对某些信号的响应,然后再打开对信号响应的屏蔽。在信号屏蔽期间,如果信号到达则处于 pending状态。

Page 294: Linux 操作系统

294

POSIX信号新增系统调用• int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact)); • int sigqueue(pid_t pid, int sig, const union sigval val); • int sigemptyset(sigset_t *set);• int sigfillset(sigset_t *set);• int sigaddset(sigset_t *set, int signum);• int sigdelset(sigset_t *set, int signum);• int sigismember(const sigset_t *set, int signum); • int sigprocmask(int how, const sigset_t *set, sigset_t *oldset));• int sigpending(sigset_t *set)); • int sigsuspend(const sigset_t *mask));

Page 295: Linux 操作系统

295

消息队列 消息是一个完整的信息单元,由消息类型和消息体组成。可以把消息看作一个记录,具有特定的格式以及特定的优先级。消息克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。消息队列是用来发送和接收消息的机制,可被全体有权限的进程访问,对消息队列有写权限的进程可以按照一定的规则向消息队列添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息。消息队列有 POSIX消息队列以及系统 V消息队列两种。

Page 296: Linux 操作系统

296

信号量 主要作为进程间以及同一进程不同线程之间对临

界区的互斥访问。

Page 297: Linux 操作系统

297

共享内存 使得多个进程可以访问同一块内存空间,是最快

的可用 IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。

Page 298: Linux 操作系统

298

套接字 更为一般的进程间通信机制,可用于不同机器之

间的进程间通信。起初是由 unix系统的 BSD分支开发出来的,但现在一般可以移植到其它类 unix系统上: linux和 system V的变种都支持套接字。

Page 299: Linux 操作系统

299

IPC相关系统调用• int pipe(int fildes[2]);• int mkfifo(const char * pathname, mode_t mode);

• void (*signal(int sig, void (*func)(int)))(int);• int kill(pid_t pid, int sig);• int raise(int signo);• int pause(void);• unsigned int sleep(unsigned int seconds);• unsigned int alarm(unsigned int seconds);

Page 300: Linux 操作系统

300

IPC相关系统调用• int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact)); • int sigqueue(pid_t pid, int sig, const union sigval val); • int sigemptyset(sigset_t *set);• int sigfillset(sigset_t *set);• int sigaddset(sigset_t *set, int signum);• int sigdelset(sigset_t *set, int signum);• int sigismember(const sigset_t *set, int signum); • int sigprocmask(int how, const sigset_t *set, sigset_t *oldset));• int sigpending(sigset_t *set)); • int sigsuspend(const sigset_t *mask));

Page 301: Linux 操作系统

301

IPC相关系统调用• key_t ftok (char *pathname, char proj); • int msgget(key_t key, int msgflg);• int msgrcv(int msqid, struct msgbuf *msgp, int msgsz, long msgtyp, int msgflg);• int msgsnd(int msqid, struct msgbuf *msgp, int msgsz, int msgflg);• int msgctl(int msqid, int cmd, struct msqid_ds *buf);

Page 302: Linux 操作系统

302

IPC相关系统调用• int semget(key_t key, int nsems, int semflg);• int semop(int semid, struct sembuf *sops, unsigned

nsops); • int semctl(int semid, int semnum, int cmd, union

semun arg);

Page 303: Linux 操作系统

303

IPC相关系统调用• int shmget(key_t key, int size, int shmflg);• void *shmat(int shmid, const void *shmaddr, int shmflg);• int shmdt(const void *shmaddr); • int shmctl(int shmid, int cmd, struct shmid_ds *buf);• void* mmap ( void * addr , size_t len , int prot , int flags , int fd , off_t offset );• int munmap( void * addr, size_t len );• int msync ( void * addr , size_t len, int flags);

Page 304: Linux 操作系统

304

系统调用 -ftok

#include <sys/types.h>#include <sys/ipc.h>

key_t ftok (char *pathname, char proj) ;

说明: ftok()用来将参数 pathname指定的文件转换为 system

V IPC所需使用的 key。参数 pathname指定的文件必须

存在且可以存取。

返回值:成功返回 key 值,否则返回 -1。

Page 305: Linux 操作系统

305

系统调用 -msgget

#include <sys/types.h>#include <sys/ipc.h> #include <sys/msg.h>

int msgget(key_t key, int msgflg) ;

说明:msgget()用来取得 key所关联的消息队列的描述符。如果参数key

为 IPC_PRIVATE则会建立新的消息队列,如果 key不为 IPC_PRIVATE也不是已建立的 IPC key,系统则会视参数msgflg

是 否有 IPC_CREAT来决定建立 IPC key为 key的消息队列。msgflg

也 用来决定消息队列的存取权限相当于 open的参数mode 。

返回值:成功返回消息队列描述符,否则返回 -1。

Page 306: Linux 操作系统

306

系统调用 -msgrcv

#include <sys/types.h>#include <sys/ipc.h> #include <sys/msg.h>

int msgrcv(int msqid, struct msgbuf *msgp, int msgsz, long msgtyp, int msgflg);

说明:msgrcv()用来从参数msqid 指定的消息队列读取消息,然后存于

参数msgp所指定的结构内。

参数msgp结构如下: struct msgbuf{

long mtype; char mtext[1];

};

Page 307: Linux 操作系统

307

系统调用 -msgrcv

参数msgsz为消息数据的长度,即mtest 参数的长度。 参数msgtyp用来指定所要读取的消息种类: =0 返回队列内第一项消息 >0 返回队列内第一项与msgtyp 相同的消息 <0 返回队列内第一项mtype 小于或等于msgtyp 绝对值的消息 参数msgflg可被设置成以下值: IPC_NOWAIT 如果没有满足条件的消息,调用立即返回,此时, errno=ENOMSG IPC_EXCEPT 与 msgtyp>0 配合使用,返回队列中第一个类型不

为 msgtyp 的消息 IPC_NOERROR 如果队列中满足条件的消息内容大于所请求的 msgsz 字节,则把该消息截断,截断部分将丢失

返回值:成功返回实际读到的信息长度,否则返回 -1。

Page 308: Linux 操作系统

308

系统调用 -msgsnd

#include <sys/types.h>#include <sys/ipc.h> #include <sys/msg.h>

int msgsnd(int msqid, struct msgbuf *msgp, int msgsz, int msgflg);

说明:msgsnd()用来向参数msqid 指定的消息队列发送消息。

参数msgp结构如下: struct msgbuf{

long mtype; char mtext[1];

};

Page 309: Linux 操作系统

309

系统调用 -msgsnd

参数msgsz为消息数据的长度,即mtest 参数的长度。

参数msgflg可被设置成 IPC_NOWAIT ,指示消息队列已满或有其他情况 无法马上送入信息时,立即返回 EAGAIN 。

返回值:成功返回 0,否则返回 -1。

Page 310: Linux 操作系统

310

系统调用 -msgctl

#include <sys/types.h>#include <sys/ipc.h> #include <sys/msg.h>

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

说明:该系统调用对由msqid 标识的消息队列执行 cmd操作,共有以下三

种 cmd操作: IPC_STAT:该命令用来获取消息队列信息,返回的信息存贮在 buf 指向的msqid 结构中;

Page 311: Linux 操作系统

311

系统调用 -msgctl

IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储 在 buf 指向的msqid 结构中,包括:msg_perm.uid、 msg_perm.gid、msg_perm.mode 以及msg_qbytes ,也影

响msg_ctime 成员。 IPC_RMID:删除msqid 标识的消息队列;

Page 312: Linux 操作系统

312

系统调用 -msgctl

struct msqid_ds {

struct ipc_perm msg_perm;

struct msg *msg_first; /* first message on queue,unused */

struct msg *msg_last; /* last message in queue,unused */ __kernel_time_t msg_stime; /* last msgsnd time */

__kernel_time_t msg_rtime; /* last msgrcv time */

__kernel_time_t msg_ctime; /* last change time */

unsigned long msg_lcbytes; /* Reuse junk fields for 32 bit */

unsigned long msg_lqbytes; /* ditto */

Page 313: Linux 操作系统

313

系统调用 -msgctl

unsigned short msg_cbytes; /* current number of bytes on

queue */

unsigned short msg_qnum; /* number of messages in queue */ unsigned short msg_qbytes; /* max number of bytes on queue

*/

__kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */ __kernel_ipc_pid_t msg_lrpid; /* last receive pid */

};

Page 314: Linux 操作系统

314

系统调用 -msgctl

struct ipc_perm{ key_t key; // 该键值则唯一对应一个消息队列uid_t uid; //消息队列的所有者标识号gid_t gid; //消息队列的组所有者标识号uid_t cuid;//创建消息队列的用户标识号gid_t cgid;//创建消息队列的组标识号mode_t mode; //消息队列的访问权限unsigned long seq;//序号

};

Page 315: Linux 操作系统

315

消息队列系统调用示例#include <sys/types.h> #include <sys/msg.h> #include <unistd.h>

void msg_stat(int,struct msqid_ds );

main() { int gflags,sflags,rflags;key_t key; int msgid;int reval;

Page 316: Linux 操作系统

316

消息队列系统调用示例struct msgsbuf{

int mtype; char mtext[1];

}msg_sbuf;

struct msgmbuf{ int mtype; char mtext[10];

}msg_rbuf;

struct msqid_ds msg_ginfo,msg_sinfo;

Page 317: Linux 操作系统

317

消息队列系统调用示例char* msgpath="/unix/msgqueue";

key=ftok(msgpath,'a'); gflags=IPC_CREAT|IPC_EXCL; msgid=msgget(key,gflags|00666);

if(msgid==-1) { printf(“msg create error\n”); return;

} //创建一个消息队列后,输出消息队列缺省属性

msg_stat(msgid,msg_ginfo); sflags=IPC_NOWAIT;

Page 318: Linux 操作系统

318

消息队列系统调用示例msg_sbuf.mtype=10; msg_sbuf.mtext[0]='a';

reval=msgsnd(msgid,&msg_sbuf,sizeof(msg_sbuf.mtext),sflags);

//发送一个消息后,输出消息队列属性 if(reval==-1) { printf(“message send error\n”); }

msg_stat(msgid,msg_ginfo);

rflags=IPC_NOWAIT|MSG_NOERROR;

reval=msgrcv(msgid,&msg_rbuf,4,10,rflags);

Page 319: Linux 操作系统

319

消息队列系统调用示例// 从消息队列中读出消息后,输出消息队列属性if(reval==-1) printf("read msg error\n"); else printf(“read from msg queue %d bytes\n”,reval); msg_stat(msgid,msg_ginfo); msg_sinfo.msg_perm.uid=8;msg_sinfo.msg_perm.gid=8;

// 此处验证超级用户可以更改消息队列的缺省msg_sinfo.msg_qbytes=16388; msg_qbytes // 注意这里设置的值大于缺省值

reval=msgctl(msgid,IPC_SET,&msg_sinfo);

Page 320: Linux 操作系统

320

消息队列系统调用示例if(reval==-1) {

printf("msg set info error\n"); return;

}

msg_stat(msgid,msg_ginfo); //验证设置消息队列属性 reval=msgctl(msgid,IPC_RMID,NULL);// 删除消息队列

if(reval==-1) { printf("unlink msg queue error\n"); return;

} }

Page 321: Linux 操作系统

321

消息队列系统调用示例void msg_stat(int msgid,struct msqid_ds msg_info) { int reval; sleep(1);// 只是为了后面输出时间的方便

reval=msgctl(msgid,IPC_STAT,&msg_info);

if(reval==-1) { printf("get msg info error\n"); return;

}

printf("\n"); printf("current number of bytes on queue is %d\n",msg_info.msg_cbytes);

Page 322: Linux 操作系统

322

消息队列系统调用示例printf("number of messages in queue is

%d\n",msg_info.msg_qnum);

// 每个消息队列的容量(字节数)都有限制 MSGMNB ,值的大小因系统而异printf("max number of bytes on queue is

%d\n“,msg_info.msg_qbytes);

printf("pid of last msgsnd is %d\n",msg_info.msg_lspid); printf("pid of last msgrcv is %d\n",msg_info.msg_lrpid); printf("last msgsnd time is %s",

ctime(&(msg_info.msg_stime)));

Page 323: Linux 操作系统

323

消息队列系统调用示例 printf("last msgrcv time is %s", ctime(&(msg_info.msg_rtime)));

printf("last change time is %s", ctime(&(msg_info.msg_ctime)));

printf("msg uid is %d\n",msg_info.msg_perm.uid); printf("msg gid is %d\n",msg_info.msg_perm.gid);

}

Page 324: Linux 操作系统

324

系统调用 -semget

#include <sys/types.h>#include <sys/ipc.h> #include <sys/sem.h>

int semget(key_t key, int nsems, int semflg);

说明: semget()用来取得 key所关联的信号量的描述符。如果参数 key

为 IPC_PRIVATE则会建立新的消息队列,参数 nsems指定打开或新

创建的信号量集中信号量的数目。如果 key不为 IPC_PRIVATE也不

是已建立的信号量 IPC key,系统则会视参数msgflg是否有 IPC_CREAT来决定建立 IPC key为 key的消息队列。msgflg也用来

决定消息队列的存取权限相当于 open的参数mode 。

返回值:成功返回信号量集描述符,否则返回 -1。

Page 325: Linux 操作系统

325

系统调用 -semget

错误码: EACCES key 指定的信号量集存在但无存取权限 EEXIST key所指的信号量集已存在 EIDRM key所指的信号量集已删除 ENOENT key所指的信号量集不存在 ENOMEM 核心内存不足 ENOSPC 已超过系统允许的信号量集的最大值

Page 326: Linux 操作系统

326

系统调用 -semop

#include <sys/types.h>#include <sys/ipc.h> #include <sys/sem.h>

int semop(int semid, struct sembuf *sops, unsigned nsops);

说明: semid是信号量集 ID, sops指向数组的每一个 sembuf 结构都刻画

一个在特定信号量上的操作。 nsops 为 sops指向数组的大小。

参数 sembuf 结构如下: struct sembuf { unsigned short sem_num;/*semaphore index in array*/ short sem_op; /* semaphore operation */ short sem_flg; /* operation flags */ };

Page 327: Linux 操作系统

327

系统调用 -semop

sem_num对应信号集中的信号量, 0对应第一个信号量。 sem_flg可取 IPC_NOWAIT以及 SEM_UNDO两个标志。如果设置了 SEM_UNDO标志,那么在进程结束时,相应的操作将被取消,这是比较重要的一个标志位。如果设置了该标志位,那么在进程没有释放共享资源就退出时,内核将代为释放。如果为一个信号量设置了该标志,内核都要分配一个 sem_undo结构来记录它,为的是确保以后资源能够安全释放。事实上,如果进程退出了,那么它所占用就释放了,但信号量值却没有改变,此时,信号量值反映的已经不是资源占有的实际情况,在这种情况下,问题的解决就靠内核来完成。这有点像僵尸进程,进程虽然退出了,资源也都释放了,但内核进程表中仍然有它的记录,此时就需要父进程调用waitpid 来解决问题了。

Page 328: Linux 操作系统

328

系统调用 -semop

sem_op的值大于 0,此值会加至 semval 。等于 0, semop会等到 semval降为 0,除非 sem_flag 含有 IPC_NOWAIT。小于 0,如 semval 大于或等于 sem_op的绝对值,则 semval 的值会减去 sem_op的绝对值,如 semval 小于 sem_op的绝对值且 sem_flag含有 IPC_NOWAIT,则返回错误。 semop同时操作多个信号量,对应多种资源的申请或释放。要么一次性获得所有资源,要么放弃申请,要么在不占有任何资源情况下继续等待,这样,一方面避免了资源的浪费;另一方面,避免了进程之间由于申请共享资源造成死锁。 信号量的当前值记录相应资源目前可用数目; sem_op>0对应进程要释放 sem_op数目的共享资源; sem_op=0用于对共享资源是否已用完的测试; sem_op<0相当于进程要申请 sem_op个共享资源。

Page 329: Linux 操作系统

329

系统调用 -semop

返回值:成功返回 0,否则返回 -1。

错误码: E2BIG 参数 nsops 大于系统允许的最大值 EACCES 无存取权限 EAGAIN 该调用无法马上处理 EIDRM 信号量队列已删除 EFAULT 参数 sops指向无效的内存地址 EFBIG 参数 sem_num小于 0或大于等于信号集合的数目 EINVAL 已超过系统允许的信号量集的最大值 EINTR 此调用被信号所中断

Page 330: Linux 操作系统

330

系统调用 -semctl

#include <sys/types.h>#include <sys/ipc.h> #include <sys/sem.h>

int semctl(int semid, int semnum, int cmd, union semun arg);

说明:该系统调用对由 semid标识的信号量集执行 cmd操作,共有以下几

种 cmd操作: IPC_STAT:把信号量集的 semid_ds 数据复制到参数 arg.buf; IPC_SET: 该命令用来设置信号量集的属性,要设置的属性存储 在 arg.buf中指向的 semid_ds结构中,包括: sem_perm.uid、 sem_perm.gid、 sem_perm.mode;

Page 331: Linux 操作系统

331

系统调用 -semctl

IPC_RMID:删除 semid标识的信号量集; GETALL:将信号量集所有的 semval值复制到参数 arg.arry GETNCNT:返回等待 semnum所代表信号灯的值增加的进程数,相

当于目前有多少进程在等待 semnum代表的信号灯所代 表的共享资源; GETPID:返回最后一个对 semnum所代表信号灯执行 semop操

作的 进程 ID GETVAL:返回参数 semnum指定信号量的 semval值 GETZCNT:返回等待 semnum所代表信号灯的值变成 0的进程数

SETALL:将信号量集所有的 semval值设置成参数 arg.arry SETVAL:将参数 semnum指定信号量的 semval值设置成 arg.va

l

Page 332: Linux 操作系统

332

系统调用 -semctl

union semun { int val; /* value for SETVAL */ struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */ unsigned short *array; /* array for GETALL & SETALL */ 、 struct seminfo *__buf; /* buffer for IPC_INFO */ void *__pad; };

Page 333: Linux 操作系统

333

系统调用 -semctl

struct semid_ds{ struct kern_ipc_perm sem_perm; /* .. see ipc.h */ time_t sem_otime; /* last semop time */ time_t sem_ctime; /* last change time */ struct sem *sem_base; /*ptr to first semaphore in array*/ struct sem_queue *sem_pending; /* pending operations to be processed */ struct sem_queue *sem_pending_last; /* last pending operation */ struct sem_undo *undo; /* undo requests on this array */ unsigned short int sem_nsems; /* no. of semaphores in array */ };

Page 334: Linux 操作系统

334

系统调用 -semctlstruct sem_queue { struct sem_queue * next; /* next entry in the queue */ struct sem_queue ** prev; /* previous entry in the queue, *(q->prev) == q */ struct task_struct* sleeper; /* this process */ struct sem_undo * undo; /* undo structure */ int pid; /* process id of requesting process */ int status; /* completion status of operation */ struct sem_array * sma;/*semaphore array for operations */ int id; /* internal sem id */ struct sembuf * sops; /* array of pending operations */ int nsops; /* number of operations */ int alter; /* operation will alter semaphore */ };

Page 335: Linux 操作系统

335

系统调用 -semctl

struct seminfo { int semmap; int semmni; int semmns; int semmnu; int semmsl; int semopm; int semume; int semusz; int semvmx; int semaem; };

Page 336: Linux 操作系统

336

信号量集系统调用示例#include <linux/sem.h> #include <stdio.h> #include <errno.h>

#define SEM_PATH "/unix/my_sem" #define max_tries 3

int semid; main() { int flag1,flag2,key,i,init_ok,tmperrno; struct semid_ds sem_info; struct seminfo sem_info2; union semun arg; //union semun

Page 337: Linux 操作系统

337

信号量集系统调用示例struct sembuf askfor_res, free_res; flag1=IPC_CREAT|IPC_EXCL|00666; flag2=IPC_CREAT|00666;

key=ftok(SEM_PATH,'a'); //error handling for ftok here;

init_ok=0;

// create a semaphore set that only includes one semphore. semid=semget(key,1,flag1);if(semid<0) { tmperrno=errno; perror("semget");

Page 338: Linux 操作系统

338

信号量集系统调用示例if(tmperrno==EEXIST) {

//flag2 只包含了 IPC_CREAT标志 , // 参数 nsems(这里为 1)必须与原来的信号灯数目一致 semid=semget(key,1,flag2); arg.buf=&sem_info;

for(i=0; i<max_tries; i++) {

if(semctl(semid, 0, IPC_STAT, arg)==-1){ perror("semctl error"); i=max_tries;

}

Page 339: Linux 操作系统

339

信号量集系统调用示例else {

if(arg.buf->sem_otime!=0){ i=max_tries; init_ok=1;

} else

sleep(1); }

}

if(!init_ok) {

Page 340: Linux 操作系统

340

信号量集系统调用示例arg.val=1; if(semctl(semid,0,SETVAL,arg)==-1)

perror("semctl setval error"); }

} else {

perror("semget error, process exit"); exit();

} } else //semid>=0; do some initializing {

Page 341: Linux 操作系统

341

信号量集系统调用示例arg.val=1; if(semctl(semid,0,SETVAL,arg)==-1)

perror("semctl setval error"); } //get some information about the semaphore and the //limit of semaphore in redhat8.0

arg.buf=&sem_info;

if(semctl(semid, 0, IPC_STAT, arg)==-1) perror("semctl IPC STAT");

Page 342: Linux 操作系统

342

信号量集系统调用示例printf("owner's uid is %d\n", arg.buf->sem_perm.uid); printf("owner's gid is %d\n", arg.buf->sem_perm.gid); printf("creater's uid is %d\n", arg.buf->sem_perm.cuid); printf("creater's gid is %d\n", arg.buf->sem_perm.cgid);

arg.__buf=&sem_info2;

if(semctl(semid,0,IPC_INFO,arg)==-1) perror("semctl IPC_INFO");

printf("the number of entries in semaphore map is %d \n", arg.__buf->semmap); printf("max number of semaphore identifiers is %d \n",

arg.__buf->semmni);

Page 343: Linux 操作系统

343

信号量集系统调用示例printf("mas number of semaphores in system is %d \n",

arg.__buf->semmns); printf("the number of undo structures system wide is %d \n",

arg.__buf->semmnu); printf("max number of semaphores per semid is %d \n",

arg.__buf->semmsl); printf("max number of ops per semop call is %d \n",

arg.__buf->semopm); printf("max number of undo entries per process is %d \n",

arg.__buf->semume); printf("the sizeof of struct sem_undo is %d \n", arg.__buf-

>semusz);

Page 344: Linux 操作系统

344

信号量集系统调用示例printf("the maximum semaphore value is %d \n", arg.__buf-

>semvmx); //now ask for available resource: askfor_res.sem_num=0; askfor_res.sem_op=-1; askfor_res.sem_flg=SEM_UNDO;

if(semop(semid,&askfor_res,1)==-1)//ask for resource perror("semop error");

sleep(3); //do some handling on the sharing resource here

printf("now free the resource\n");

Page 345: Linux 操作系统

345

信号量集系统调用示例//now free resource free_res.sem_num=0; free_res.sem_op=1; free_res.sem_flg=SEM_UNDO;

if(semop(semid,&free_res,1)==-1)//free the resource. if(errno==EIDRM)

printf("the semaphore set was removed\n"); if(semctl(semid, 0, IPC_RMID)==-1)

perror("semctl IPC_RMID"); else

printf("remove sem ok\n"); }

Page 346: Linux 操作系统

346

系统调用 -shmget

#include <sys/types.h>#include <sys/ipc.h> #include <sys/shm.h>

int shmget(key_t key, int size, int shmflg) ;

说明: shmget()用来取得 key所关联的共享内存的描述符。如果参数key

为 IPC_PRIVATE则会建立新的共享内存,其大小由参数 size决定 如果 key不为 IPC_PRIVATE也不是已建立的共享内存 IPC key,

系 统则会视参数 shmflg是否有 IPC_CREAT来决定建立 IPC key为 k

ey 的共享内存。 shmflg也用来决定共享内存的存取权限相当于 open 的参数mode 。

返回值:成功返回共享内存描述符,否则返回 -1。

Page 347: Linux 操作系统

347

系统调用 -shmget

错误码: EINVAL 参数 size 小于 SHMMIN或大于 SHMMAX EACCES key 指定的共享内存存在但无存取权限

EEXIST key所指的共享内存已存在 EIDRM key所指的共享内存已删除 ENOENT key所指的共享内存不存在 ENOMEM 核心内存不足 ENOSPC 已超过系统允许的共享内存的最大值SHMALL

Page 348: Linux 操作系统

348

系统调用 -shmat

#include <sys/types.h>#include <sys/ipc.h> #include <sys/shm.h>

void *shmat(int shmid, const void shmaddr, int shmflg);

说明: shmat()用来将 shmid所指的共享内存接入进程的虚地址空间。 参数: shmaddr=0 核心自动选择一个地址; shmaddr≠0 参数 shmflg也不包含SHM_RND标识,则共享内存

接 入 shmaddr 地址 shmaddr≠0 参数 shmflg 包含 SHM_RND标识,则参数 shmaddr 会自

动调整为 SHMLBA的整数倍; 返回值:成功返回连接好的虚地址,否则返回 -1。

Page 349: Linux 操作系统

349

系统调用 -shmdt

#include <sys/types.h>#include <sys/ipc.h> #include <sys/shm.h>

int shmdt(const void shmaddr);

说明: shmdt()用来将连接到 shmaddr的共享内存与进程的虚地址空间

断接。 返回值:成功 0,否则返回 -1。

Page 350: Linux 操作系统

350

系统调用 -shmctl

#include <sys/types.h>#include <sys/ipc.h> #include <sys/shm.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

说明: shmctl 系统调用对由 shmid标识的共享内存执行 cmd操作,共有以

下几种 cmd操作: IPC_STAT:把共享内存的 shmid_ds 数据复制到参数 buf; IPC_SET: 该命令用来设置共享内存的属性,要设置的属性存储 在 buf中指向的 shmid_ds结构中,包括: shm_perm.uid、 shm_perm.gid、 shm_perm.mode;

Page 351: Linux 操作系统

351

系统调用 -shmctl

IPC_RMID:删除 shmid标识的共享内存;

SHM_LOCK:禁止共享内存兑换到 swap,超级用户可用; SHM_UNLOCK:允许共享内存兑换到 swap,超级用户可用;

Page 352: Linux 操作系统

352

系统调用 -shmctl

struct shmid_ds{ struct kern_ipc_perm sem_perm; /* .. see ipc.h */ int shm_segsz; /* 共享内存大小 */ time_t shm_atime; /* 最后一次 attach该共享内存的时间 */ time_t sem_dtime; /* 最后一次 detach该共享内存的时间 */ time_t sem_ctime; /* 最后一次更改该共享内存的时间 */ unsigned short shm_cpid; /* 建立该共享内存的进程标识号 */ unsigned short shm_lpid; /* 最后操作该共享内存的进程标识号 */

short shm_nattch; unsigned short shm_npages; unsigned long *shm_pages; struct shm_decs *attaches;};

Page 353: Linux 操作系统

353

共享内存系统调用示例#include <unistd.h> #include <sys/ipc.h> #include <sys/shm.h>

#define KEY 1234 #define SIZE 1024

main() { int shmid; char *shmaddr;struct shmid_ds buf;

Page 354: Linux 操作系统

354

共享内存系统调用示例shmid=shmget(KEY, SIZE, IPC_CREAT|0600); /* 建立共享内存 */

if(fork()==0){

shmaddr=(char *)shmat(shmid, NULL, 0);strcpy(shmaddr, “Hi, I am child process!\n”);shmdt(shmaddr);return;

}else{

sleep(3);shmctl(shmid, IPC_STAT, &buf);

Page 355: Linux 操作系统

355

共享内存系统调用示例printf(“shm_segsz = %d bytes\n”, buf.shm_segsz);printf(“shm_cpid = %d bytes\n”, buf.shm_cpid);printf(“shm_lpid = %d bytes\n”, buf.shm_lpid);shmaddr=(char *)shmat(shmid, NULL, 0);printf(“%s”, shmaddr);shmdt(shmaddr);shmctl(shmid, IPC_RMID, NULL);

}}

Page 356: Linux 操作系统

356

系统调用 -mmap

#include <unistd.h>#include <sys/mman.h>

void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offsize);

说明:mmap()用来将某个文件内容映射到内存中,对该内存区域的存取

即是直接对该文件内容的读写。参数 start 指向欲对应的内存起 始地址,通常设为 NULL,代表让系统自动选定地址,对应成功后 该地址会返回。参数 length 代表将文件中多大的部分对应到内 存。

Page 357: Linux 操作系统

357

系统调用 -mmap

参数: prot代表映射区域的保护方式有下列组合

PROT_EXEC 映射区域可被执行

PROT_READ 映射区域可被读取

PROT_WRITE 映射区域可被写入

PROT_NONE 映射区域不能存取

Page 358: Linux 操作系统

358

系统调用 -mmap

参数: flags会影响映射区域的各种特性

MAP_FIXED 如果参数 start所指的地址无法成功建立映射时,则 放弃映射,不对地址做修正。 MAP_SHARED 对映射区域的写入数据会复制回文件内,而且允许 其他映射该文件的进程共享。

MAP_PRIVATE 对映射区域的写入操作会产生一个映射文件的复 制,即私人的“写入时复制”( copy on write)对 此区域作的任何修改都不会写回原来的文件内容 MAP_ANONYMOUS 建立匿名映射。此时会忽略参数 fd,不涉及

文 件,而且映射区域无法和其他进程共享。

Page 359: Linux 操作系统

359

系统调用 -mmap

参数: MAP_DENYWRITE 只允许对映射区域的写入操作,其他对文件直接

写入的操作将会被拒绝。

MAP_LOCKED 将映射区域锁定住,这表示该区域不会被对换。

在调用mmap()时必须要指定MAP_SHARED 或MAP_PRIVATE。

参数: fd为 open() 返回的文件描述词,代表欲映射到内存的文件。参数: offset为文件映射的偏移量,通常设置为 0,代表从文件最前方 开始对应, offset必须是分页大小的整数倍。

返回值:若映射成功则返回映射区的内存起始地址,否则返回 MAP_FAILED(- 1),错误原因存于 errno 中。

Page 360: Linux 操作系统

360

系统调用 -mmap

错误码: EBADF 参数 fd 不是有效的文件描述词 EACCES 存取权限有误。如果是MAP_PRIVATE 情况下文件必须可

读,使用MAP_SHARED则要有 PROT_WRITE以及该文件要 能写入。

EINVAL 参数 start、 length 或 offset有一个不合法。 EAGAIN 文件被锁住,或是有太多内存被锁住。 ENOMEM 内存不足。 EINVAL 参数 size 小于 SHMMIN或大于 SHMMAX

Page 361: Linux 操作系统

361

系统调用 -munmap

#include <unistd.h>#include <sys/mman.h>

int munmap(void *start, size_t length);

说明:munmap()用来取消参数 start所指的映射内存起始地址,参数 length 则是欲取消的内存大小。当进程结束或利用 exec 相关函数 来执行其他程序时,映射内存会自动解除,但关闭对应的文件描 述符时不会解除映射。

返回值:如果解除映射成功则返回 0,否则返回- 1。

错误码: EINVAL 参数 start或 length 不合法。

Page 362: Linux 操作系统

362

内存映射系统调用示例#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>#include<sys/mman.h>

main(){int fd;void *start;struct stat sb;

fd=open(“/etc/passwd”,O_RDONLY); /* 打开 /etc/passwd*/

Page 363: Linux 操作系统

363

内存映射系统调用示例fstat(fd,&sb); /* 取得文件大小 */

start=mmap(NULL,sb.st_size,PROT_READ,MAP_PRIVATE,fd,0);

if(start= = MAP_FAILED) /*判断是否映射成功 */return;

printf(“%s”,start);

munma(start,sb.st_size); /*解除映射 */

closed(fd);}

Page 364: Linux 操作系统

364

设备驱动程序 设备驱动程序是操作系统内核代码的一部分,位

于系统调用与物理设备之间,为操作系统访问设备提供一致的、抽象的接口,屏蔽具体物理设备的操作细节,根据操作系统的要求完成系统与物理设备之间的数据传输。

Page 365: Linux 操作系统

365

设备驱动程序的特点• 运行于系统核心地址空间• 可被多个进程共享• 为请求提供服务• 与操作系统其他部分配合完成系统调用• 设备驱动程序错误会引起系统瘫痪

Page 366: Linux 操作系统

366

内核功能模块的划分• 进程管理 内核负责创建和终止进程,并且处理它们和外部

世界的联系(输入和输出)。对整个系统功能来讲,不同进程之间的通信(通过信号,管道,进程间通信原语)是基本的,这也是由内核来处理的。另外,调度器,可能是整个操作系统中最关键的例程,是进程管理中的一部分。更广广义的说,内核的进程管理活动实现了在一个 CPU上多个进程的抽象概念。

Page 367: Linux 操作系统

367

内核功能模块的划分• 内存管理 计算机内存是主要资源,而使用内存的策略是影响整个系统性能的关键。内核为每个进程在有限可利用的资源上建立了虚拟地址空间。内核不同部分通过一组函数与内存管理子系统交互,这些包括从简单的malloc/free到更稀奇古怪的功能。

Page 368: Linux 操作系统

368

内核功能模块的划分• 文件系统 Unix系统是建立在文件系统这个概念上的;Unix里几乎所有东西都可以看作文件。内核在非结构的硬件上建立了结构化的文件系统,这个抽象的文件被系统广泛应用。另外, Linux支持多文件系统类型,即,物理介质上对数据的不同组织方法。

Page 369: Linux 操作系统

369

内核功能模块的划分• 设备控制 几乎每种系统操作最后都要映射到物理设备

上。除了处理器,内存和少数其他实体外,几乎所有设备的控制操作都由设备相关的代码来实现。这些代码就是设备驱动程序。内核必须为每个外部设备嵌入设备驱动程序,从硬盘驱动器到键盘和磁带。内核的这方面功能就是本书的着眼点。

Page 370: Linux 操作系统

370

内核功能模块的划分• 网络 网络必须由操作系统来管理,由于大多数网络操作不是针对于进程的:接收数据包是异步事件。数据包必须在进程处理它们以前就被收集,确认和分发。系统通过程序和网络接口发送数据包,并且应该可以正确地让程序睡眠,并唤醒等待网络数据的进程。另外,所有路由和地址解析问题是在内核里实现的。

Page 371: Linux 操作系统

371

设备驱动程序与其他部分的关系

Page 372: Linux 操作系统

372

设备驱动程序的分类• 字符设备 可以象文件一样访问字符设备,字符设备驱动程

序通常会实现 open, close , read和write系统调用。系统控制台和并口就是字符设备的例子,它们可以很好地用流概念描述。通过文件系统节点可以访问字符设备,例如 /dev/tty1和 /dev/lp1。在字符设备和普通文件系统间的唯一区别是:普通文件允许在其上来回读写,而大多数字符设备仅仅是数据通道,只能顺序读写。当然,也存在这样的字符设备,看起来象个数据区,可以来回读取其中的数据。

Page 373: Linux 操作系统

373

设备驱动程序的分类• 块设备 块设备是文件系统的宿主,如磁盘。以块为单位

进行访问,一个块通常是 1K字节数据。块设备和字符设备只在内核内部的管理上有所区别,也就是在内核 /驱动程序间的软件接口上有所区别。每个块设备也通过文件系统节点来读写数据,它们之间的不同对用户来说是透明的。块设备驱动程序和内核的接口和字符设备驱动程序的接口是一样的,它也通过一个传统的面向块的接口与内核通信,但这个接口对用户来说时不可见的。

Page 374: Linux 操作系统

374

设备驱动程序的分类• 网络设备 任何网络事务处理都是通过网络接口实现的,通常,接口是一个硬件

设备,但也可以象 loopback (回路)接口一样是软件工具。网络接口是由内核网络子系统驱动的,它负责发送和接收数据包,而且无需了解每次事务是如何映射到实际被发送的数据包。尽管“ telnet”和“ ftp”连接都是面向流的,它们使用同样的设备进行传输;但设备并没有看到任何流,仅看到数据报。由于不是面向流的设备,所以网络接口不能象 /dev/tty1 那样简单地映射到文件系统的节点上。 Unix调用这些接口的方式是给它们分配一个独立的名字(如 eth0)。这样的名字在文件系统中并没有对应项。内核和网络设备驱动程序之间的通信与字符设备驱动程序和块设备驱动程序与内核间的通信是完全不一样的。内核不再调用 read,write ,它调用与数据包传送相关的函数。

Page 375: Linux 操作系统

375

设备驱动程序的特点• 运行于系统核心地址空间• 可被多个进程共享• 为请求提供服务• 与操作系统其他部分配合完成系统调用• 设备驱动程序错误会引起系统瘫痪

Page 376: Linux 操作系统

376

设备驱动程序的注意事项• 只能使用核心提供的功能函数• 代码应为可重入的• 提供机制(mechanism),不实现策略(policy )

• 不影响系统安全

Page 377: Linux 操作系统

377

设备驱动程序的相关概念• 主设备号与次设备号 系统使用主设备号和次设备号标识一个设备,一般主设备号用于标识设备驱动程序,次设备号用于标识同一个设备驱动程序管理的不同设备。

Page 378: Linux 操作系统

378

设备驱动程序的相关概念 核心用定义于 <linux/types.h>中的类型 dev_t 表示设备号,为一个 32位整数, 12位用于主设备号,其余 20位用于次设备号。核心使用定义于 <linux/kdev_t.h>中的宏对设备号进行访问 :

MAJOR(dev_t dev); MINOR(dev_t dev); MKDEV(int major, int minor);

Page 379: Linux 操作系统

379

设备驱动程序的相关概念• 设备文件 块设备和字符设备被组织到文件系统中,通过文

件系统调用进行访问。每一个设备由一个设备文件表示,设备文件存储设备的主设备号和次设备号,访问权限等等。

Page 380: Linux 操作系统

380

设备驱动程序的相关概念

Page 381: Linux 操作系统

381

设备驱动程序的相关概念• 模块(module) 模块是可以运行时被加载到内核的功能模块,加载后模块作为内核功能的一部分运行于核心态,模块在不使用的时候,可以被动态地从核心卸载。模块一般实现为共享库。

Page 382: Linux 操作系统

382

设备驱动程序的相关概念• 模块的装载 模块的装载使用命令 insmod或modprobe ,insmod装载一个模块,modprobe装载有依赖关系的所有模块。 insmod和modprobe 使用系统调用 sys_init_module 完成模块的装载。

系统调用 sys_init_module首先为模块分配系统内存,然后将模块正文拷贝进核心内存,接下来进行符号解析,最后调用模块的初始化函数。

Page 383: Linux 操作系统

383

设备驱动程序的相关概念#include <linux/init.h>#include <linux/module.h>

MODULE_LICENSE("Dual BSD/GPL");

static int hello_init(void){printk(KERN_ALERT "Hello, world\n");return 0;

}

Page 384: Linux 操作系统

384

设备驱动程序的相关概念static void hello_exit(void){printk(KERN_ALERT "Goodbye, cruel world\n");

}

module_init(hello_init);module_exit(hello_exit);

Page 385: Linux 操作系统

385

设备驱动程序的相关概念加载模块命令: insmod ./hello.ko

卸载模块命令: rmmod hello

Page 386: Linux 操作系统

386

设备驱动程序的相关概念• 内核符号表 为支持模块的动态加载与卸载核心维护一个内核

符号表,内核符号表记录所有核心导出的符号(函数和全局变量)及其地址。

Page 387: Linux 操作系统

387

设备驱动程序的相关概念• 内核符号表 为支持模块的动态加载与卸载核心维护一个内核

符号表,内核符号表记录所有核心导出的符号(函数和全局变量)及其地址。被装载的模块也可以将自身的全局变量输出到核心符号表中,但要注意符号污染问题。

EXPORT_SYMBOL(name); EXPORT_SYMBOL_GPL(name);

Page 388: Linux 操作系统

388

设备驱动程序的相关概念• 用户空间与核心空间 核心需要在核心地址空间和用户空间交换数据,核心代码使用用户空间的地址要做仔细的检查。

Page 389: Linux 操作系统

389

设备驱动程序的相关概念访问用户地址空间:

unsigned long copy_to_user(void __user *to, const void *from,unsigned long count);

unsigned long copy_from_user(void *to, const void __user *from,unsigned long count);

Page 390: Linux 操作系统

390

设备驱动程序的相关概念 分配核心内存:

void *kmalloc(size_t size, int flags); void kfree(void *ptr);

Page 391: Linux 操作系统

391

设备驱动程序的相关概念• 当前进程 驱动程序有时需要访问当前进程的信息。当前进

程是正在执行系统调用的进程。

Page 392: Linux 操作系统

392

设备驱动程序的相关概念 对当前进程的访问可以通过 current 指针。 current 指

向 struct task_struct 结构。

#include <asm/current.h>#include <linux/sched.h>

printk(KERN_INFO “The process is \”%s\“ (pid %i)\n",

current->comm, current->pid);

Page 393: Linux 操作系统

393

设备驱动程序的相关概念• 核心栈 模块与操作系统核心共享核心栈,核心栈较小,

一般为一个页面大小,因此不要在模块中使用大的临时变量。

Page 394: Linux 操作系统

394

设备驱动程序的相关概念• 初始化函数 初始化函数在模块被加载的时候调用,调用完以

后所占用的空间被系统释放,因此不要在模块中调用初始化函数。

Page 395: Linux 操作系统

395

设备驱动程序的相关概念

static int __init initialization_function(void){/* Initialization code here */

}

module_init(initialization_function);

Page 396: Linux 操作系统

396

设备驱动程序的相关概念• 清除函数 清除函数在模块被删除的时候调用,主要用于释放模块申请的系统资源。

Page 397: Linux 操作系统

397

设备驱动程序的相关概念

static void __exit cleanup_function(void){/* Cleanup code here */

}

module_exit(cleanup_function);

Page 398: Linux 操作系统

398

设备驱动程序的相关概念• 模块参数 模块经常需要一些入口参数指明模块应如何动作。

linux支持模块定义在被加载时要接收的参数。

注:模块和模块参数信息将被记录在文件 /proc/modules 和文件 /sys/module 中

Page 399: Linux 操作系统

399

设备驱动程序的相关概念static char *whom = "world";static int howmany = 10;

module_param(howmany, int, S_IRUGO);module_param(whom, charp, S_IRUGO);

命令行:

insmod hellop howmany=10 whom="Mom"

Page 400: Linux 操作系统

400

设备驱动程序的相关概念支持的模块参数类型:

boolintlongshortuintulongushortcharp

Page 401: Linux 操作系统

401

设备驱动程序的相关概念• 模块装载的竞争条件 模块一旦被注册则立即可被系统访问,有可能出

现初始化过程尚未结束时候,系统引用已经被注册的部分。

初始化失败的时候,系统可能已经引用了被注册的功能。

Page 402: Linux 操作系统

402

主设备号的分配和释放 建立设备驱动程序首先需要为设备分配一个主设备号。 linux提供用户指定和动态分配两种方式来分配主设备号。

Page 403: Linux 操作系统

403

主设备号的分配和释放静态分配使用如下函数: #include <linux/fs.h>

int register_chrdev_region(dev_t first, unsigned int count, char *name);

first 为申请分配的连续设备号的起始设备号count 为申请分配的设备号数量name 为与设备号相关联的设备名

注:设备名将被记录在文件 /proc/devices 文件中

Page 404: Linux 操作系统

404

主设备号的分配和释放动态分配使用如下函数: #include <linux/fs.h>

int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name);

dev 保存被分配的第一个主设备号firstminor 为申请的次设备号,通常为 0

Page 405: Linux 操作系统

405

主设备号的分配和释放 如果不在使用一个设备号的时候,需要将设备号释放。设备号的释放使用如下函数: #include <linux/fs.h>

void unregister_chrdev_region(dev_t first, unsigned int count);

Page 406: Linux 操作系统

406

用动态分配的主设备号创建设备 动态分配的主设设备号无法用来预先创建设备文件,因此

为动态分配主设备号的设备创建文件需要用文件 /proc/devices 。 /proc/devices 文件内容如下: Character devices:1 mem2 pty3 ttyp

Block devices:2 fd8 sd11 sr

Page 407: Linux 操作系统

407

用动态分配的主设备号创建设备 为动态分配设备号的设备创建文件,需要使用脚本文件在insmod模块后,使用 /proc/devices 的设备号创建文件。

#!/bin/sh

module="scull"device="scull"mode="664"

/sbin/insmod ./$module.ko $* || exit 1

rm -f /dev/${device}[0-3]

Page 408: Linux 操作系统

408

用动态分配的主设备号创建设备major=$(awk "\\$2= =\"$module\" {print \\$1}" /proc/devices)

mknod /dev/${device}0 c $major 0mknod /dev/${device}1 c $major 1mknod /dev/${device}2 c $major 2mknod /dev/${device}3 c $major 3

group="staff"grep -q '^staff:' /etc/group || group="wheel"

chgrp $group /dev/${device}[0-3]chmod $mode /dev/${device}[0-3]

Page 409: Linux 操作系统

409

灵活地创建设备 创建设备的较为理想的方法是缺省使用动态分配的设备号,保留指定设备号的能力。

Page 410: Linux 操作系统

410

灵活地创建设备if (scull_major) {dev = MKDEV(scull_major, scull_minor);result = register_chrdev_region(dev,

scull_nr_devs, "scull");} else {result = alloc_chrdev_region(&dev, scull_minor,

scull_nr_devs, "scull");scull_major = MAJOR(dev);

}

if (result < 0) {printk(KERN_WARNING "scull: can't get major %d\n", scull_major);return result;

}

Page 411: Linux 操作系统

411

设备驱动程序相关的核心数据结构 基本的设备驱动程序需要引用三个核心数据结构

• file_operations

用来建立设备驱动程序的操作与设备文件的对应关系。是由一些列函数指针组成的结构。被打开的设备文件的文件表中有一个域( f_op)指向 file_operations结构。该结构中的函数主要用于实现对设备文件的 open、 read、write 等系统调用。 file_operations结构和指向该结构的指针一般被称为 fops 。

Page 412: Linux 操作系统

412

设备驱动程序相关的核心数据结构• file

每一个 file结构表示一个被打开的文件,它由 open系统调用创建,并被传送给每一个对文件操作的函数。一般用file 和 filp分别指file结构和指向 file结构的指针。

• inode

内核使用 inode结构表示一个文件。

Page 413: Linux 操作系统

413

file_operations结构struct file_operations{

int (*lseek)(struct inode *inode,struct file *flip,off_t off,int pos);int (*read)(struct inode *inode,struect file *flip,char *buf,int count);int (*write)(struct inode *inode,struct file *flip,char *buf,int count);int (*readdir)(struct inode *inode,struct file *filp,struct dirent *dirent,int count);int (*select)(struct inode *inode,struct file *filp,

int sel_type,select_table *wait);int (*ioctl)(struct inode *inode,struct file *filp,

unsigned int cmd,unsigned int arg);int (*mmap)(void);int (*open)(struct inode *inode,struct file *filp);void (*release)(struct inode *inode,struct file *filp);int (*fsync)(struct inode *inode,struct file *filp);

};

Page 414: Linux 操作系统

414

file结构struct file { mode_t f_mode; dev_t f_rdev; /* needed for /dev/tty */ off_t f_pos; /* Curr. posn in file */ unsigned short f_flags; /* The flags arg passed to open */ unsigned short f_count; /* Number of opens on this file */ unsigned short f_reada; struct inode *f_inode; /* pointer to the inode struct */ struct file_operations *f_op;/* pointer to the fops struct*/};

Page 415: Linux 操作系统

415

inode结构struct inode { dev_t i_dev; unsigned long i_ino; /* Inode number */ umode_t i_mode; /* Mode of the file */ nlink_t i_nlink; uid_t i_uid; gid_t i_gid; dev_t i_rdev; /* Device major and minor numbers*/ off_t i_size; time_t i_atime; time_t i_mtime; time_t i_ctime; unsigned long i_blksize; unsigned long i_blocks; struct inode_operations * i_op; struct super_block * i_sb;

Page 416: Linux 操作系统

416

inode结构 struct wait_queue * i_wait; struct file_lock * i_flock; struct vm_area_struct * i_mmap; struct inode * i_next, * i_prev; struct inode * i_hash_next, * i_hash_prev; struct inode * i_bound_to, * i_bound_by; unsigned short i_count; unsigned short i_flags; /* Mount flags (see fs.h) */ unsigned char i_lock; unsigned char i_dirt; unsigned char i_pipe; unsigned char i_mount; unsigned char i_seek; unsigned char i_update;

Page 417: Linux 操作系统

417

inode结构 union { struct pipe_inode_info pipe_i; struct minix_inode_info minix_i; struct ext_inode_info ext_i; struct msdos_inode_info msdos_i; struct iso_inode_info isofs_i; struct nfs_inode_info nfs_i; } u;}; 从索引节点获得设备号 :

unsigned int iminor(struct inode *inode); unsigned int imajor(struct inode *inode);

Page 418: Linux 操作系统

418

字符设备驱动程序 -scull

scull 设备是一个虚拟字符设备,对该设备的读写实现为对内存的读写。

scull 设备实现以下操作:

struct file_operations scull_fops = { owner = THIS_MODULE; llseek = scull_llseek; read = scull_read; write = scull_write; ioctl = scull_ioctl; open = scull_open;

release = scull_release; };

Page 419: Linux 操作系统

419

字符设备驱动程序 -scull

scull 设备的存储结构如下:

Page 420: Linux 操作系统

420

字符设备驱动程序 -scull

scull 设备的存储结构如下:

struct scull_qset { void **data; struct scull_qset *next; };

Page 421: Linux 操作系统

421

字符设备驱动程序 -scull

系统在内部使用结构 struct cdev表示一个字符设备,为使系统能够引用字符设备的操作,字符设备必须分配、初始化、注册一个struct cdev 结构。

struct cdev *my_cdev = cdev_alloc( );

my_cdev->ops = &my_fops;

void cdev_init(struct cdev *cdev, struct file_operations *fops);

int cdev_add(struct cdev *dev, dev_t num, unsigned int count);

void cdev_del(struct cdev *dev);

Page 422: Linux 操作系统

422

字符设备驱动程序 -scull

struct scull_dev {struct scull_qset *data; /* Pointer to first quantum

set */int quantum; /* the current quantum size */int qset; /* the current array size */unsigned long size; /* amount of data stored here */unsigned int access_key; /* used by sculluid and

scullpriv */struct semaphore sem; /* mutual exclusion semaphore */struct cdev cdev; /* Char device structure */

};

Page 423: Linux 操作系统

423

字符设备驱动程序 -scull

static void scull_setup_cdev(struct scull_dev *dev, int index){int err, devno = MKDEV(scull_major, scull_minor + index);cdev_init(&dev->cdev, &scull_fops);dev->cdev.owner = THIS_MODULE;dev->cdev.ops = &scull_fops;err = cdev_add (&dev->cdev, devno, 1);/* Fail gracefully if need be */if (err)

printk(KERN_NOTICE "Error %d adding scull%d", err, index);}

Page 424: Linux 操作系统

424

字符设备驱动程序 -scull

container_of(pointer, container_type, container_field);

struct scull_dev *dev; /* device information */dev = container_of(inode->i_cdev, struct scull_dev, cdev);

filp->private_data = dev; /* for other methods */

Page 425: Linux 操作系统

425

字符设备驱动程序 -scull

int scull_open(struct inode *inode, struct file *filp){ struct scull_dev *dev; /* device information */ dev = container_of(inode->i_cdev, struct scull_dev, cdev);

filp->private_data = dev; /* for other methods */

/* now trim to 0 the length of the device if open was write-only */

if ( (filp->f_flags & O_ACCMODE) = = O_WRONLY) { scull_trim(dev); /* ignore errors */ }

return 0; /* success */}

Page 426: Linux 操作系统

426

字符设备驱动程序 -scull

int scull_trim(struct scull_dev *dev){ struct scull_qset *next, *dptr; int qset = dev->qset; /* "dev" is not-null */ int i;

for (dptr = dev->data; dptr; dptr = next) {

if (dptr->data) { for (i = 0; i < qset; i++) kfree(dptr->data[i]); kfree(dptr->data); dptr->data = NULL; }

Page 427: Linux 操作系统

427

字符设备驱动程序 -scull

next = dptr->next; kfree(dptr); }

dev->size = 0; dev->quantum = scull_quantum; dev->qset = scull_qset; dev->data = NULL;

return 0;}

Page 428: Linux 操作系统

428

字符设备驱动程序 -scull

int scull_release(struct inode *inode, struct file *filp){ return 0;}