描述
开 本: 16开纸 张: 胶版纸包 装: 平装-胶订是否套装: 否国际标准书号ISBN: 9787302520252丛书名: 高等学校计算机专业规划教材
产品特色
编辑推荐
循序渐进地进入Linux环境下系统级程序设计
内容简介
本书介绍使用C语言结合Linux API进行系统级程序设计的方法,主要包括Linux基础知识、C程序开发工具、文件及目录管理、进程管理、重定向与管道、信号、进程间通信、线程、线程间的同步机制、网络程序设计等10章,以及10个实验,全面而系统地介绍Linux操作系统各种机制的实现原理、经常使用的系统接口函数、系统接口和命令程序之间的关系以及命令程序的实现过程等。
本书结构清晰,适合于教学,为各类高等学校开设开源软件程序设计课程提供了一个切实可行的思路,同时也可作为培训教材在各类培训机构使用。书中各章节划分明确,各章突出不同的重点,有利于教师组织安排授课内容;同时提供设计精美、内容丰富的电子教案以及教学素材供授课教师使用,有效地减轻了授课教师备课的工作量和强度。
本书目标读者为具有一定C语言基础的读者,适合各类高等院校的计算机及相关专业学生、Linux培训机构、Linux API编程爱好者、Linux程序开发人员及爱好者学习使用。
本书结构清晰,适合于教学,为各类高等学校开设开源软件程序设计课程提供了一个切实可行的思路,同时也可作为培训教材在各类培训机构使用。书中各章节划分明确,各章突出不同的重点,有利于教师组织安排授课内容;同时提供设计精美、内容丰富的电子教案以及教学素材供授课教师使用,有效地减轻了授课教师备课的工作量和强度。
本书目标读者为具有一定C语言基础的读者,适合各类高等院校的计算机及相关专业学生、Linux培训机构、Linux API编程爱好者、Linux程序开发人员及爱好者学习使用。
目 录
目录
第1章Linux基础知识/1
1.1Linux简介1
1.1.1Linux系统的发展1
1.1.2与Linux相关的一些知识3
1.2Linux系统编程5
1.2.1什么是系统编程5
1.2.2系统编程的学习内容及方法6
1.2.3一个例子7
1.2.4系统调用和库函数10
1.3常用工具及命令10
1.3.1命令格式10
1.3.2常用工具11
1.3.3常用命令15
1.3.4获取帮助17
1.4小结20
习题20
第2章C程序开发工具/22
2.1编辑工具22
2.1.1编辑工具介绍22
2.1.2vi和vim程序编辑器25
2.2gcc编译器34
2.3gdb调试器36
2.3.1启动和退出gdb37
2.3.2显示和查找程序源代码38
2.3.3执行程序和获取帮助39
2.3.4设置和管理断点40
2.3.5查看和设置变量的值45
2.3.6控制程序的执行46
2.4make和Makefile48〖1〗Linux环境高级程序设计目录[3]〖3〗2.4.1make命令48
2.4.2编写Makefile文件50
2.5小结54
习题55
第3章文件及目录管理/56
3.1文件和I/O操作分类56
3.1.1文件概念56
3.1.2文件操作分类56
3.2Linux文件系统概述58
3.2.1文件结构58
3.2.2文件系统模型59
3.2.3目录、索引结点和文件描述符60
3.2.4文件的分类63
3.2.5文件访问权限控制64
3.3文件的读写68
3.3.1文件打开、创建和关闭69
3.3.2文件的读写72
3.3.3文件读写指针的移动80
3.3.4标准I/O的文件流82
3.4文件属性及相关系统调用87
3.4.1获取文件属性87
3.4.2修改文件的访问权限91
3.4.3修改文件的用户属性93
3.4.4获取用户的信息94
3.4.5改变文件大小95
3.4.6获取文件的时间属性96
3.5目录操作97
3.5.1打开目录97
3.5.2读取目录项98
3.5.3关闭目录98
3.6实现自己的ls命令100
3.7小结105
习题106
第4章进程管理/107
4.1Linux可执行程序的存储结构与进程结构107
4.1.1Linux可执行程序的存储结构107
4.1.2Linux系统的进程结构109
4.1.3进程树110
4.2进程的环境和进程属性111
4.2.1进程的环境111
4.2.2进程的状态112
4.2.3进程的基本属性115
4.2.4进程的用户属性121
4.3进程管理124
4.3.1创建进程124
4.3.2在进程中运行新代码127
4.3.3vfork函数131
4.3.4进程退出133
4.3.5wait函数138
4.3.6Shell的实现流程142
4.4Linux中的特殊进程143
4.4.1孤儿进程143
4.4.2僵尸进程144
4.4.3守护进程145
4.4.4出错记录148
4.5小结150
习题150
第5章重定向与管道/151
5.1重定向和管道命令151
5.1.1重定向命令151
5.1.2管道命令152
5.2实现重定向153
5.2.1重定向的实施者153
5.2.2实现重定向的前提条件154
5.2.3dup和dup2154
5.2.4重定向的三种方法157
5.2.5ls l>list.txt159
5.3管道编程161
5.3.1匿名管道161
5.3.2命名管道165
5.3.3ls l|grep root168
5.3.4popen和pclose170
5.4小结173
习题174
第6章信号/175
6.1信号概述175
6.1.1什么是信号175
6.1.2信号的来源和处理过程177
6.1.3信号的处理方式177
6.2早期信号处理函数——signal178
6.2.1signal函数实现信号的三种处理方式178
6.2.2signal函数存在的问题182
6.3信号处理函数——sigaction183
6.3.1sigaction系统调用183
6.3.2sigaction函数参数的说明186
6.4信号其他相关函数190
6.4.1kill与raise190
6.4.2alarm与pause192
6.4.3实现sleep函数193
6.5小结194
习题195
第7章进程间通信/196
7.1选择进程间通信方式196
7.1.1文件实现进程间通信196
7.1.2命名管道实现进程间通信199
7.2共享内存201
7.2.1什么是共享内存201
7.2.2共享内存相关系统调用203
7.2.3共享内存实现进程间通信206
7.2.4三种通信方式的比较208
7.3信号量209
7.3.1信号量及相关系统调用209
7.3.2使用信号量控制对共享内存的访问214
7.3.3信号量机制总结221
7.4System V IPC222
7.4.1Linux中的进程通信机制222
7.4.2System V IPC概述223
7.4.3IPC的标识符和键224
7.5消息队列225
7.5.1消息队列的概念225
7.5.2消息队列相关系统调用226
7.5.3使用消息队列实现进程间通信229
7.6小结232
习题232
第8章线程/233
8.1线程概述233
8.1.1线程的定义233
8.1.2用户级线程和内核级线程234
8.1.3线程与进程的对比234
8.2线程基本操作235
8.2.1线程创建235
8.2.2线程退出/等待238
8.2.3线程终止244
8.2.4线程挂起247
8.2.5线程的分离249
8.2.6线程的一次性初始化251
8.2.7线程的私有数据253
8.3线程属性257
8.3.1线程属性对象258
8.3.2设置/获取线程detachstate属性260
8.3.3设置与获取线程栈相关属性261
8.4线程应用举例266
8.5小结273
习题273
第9章线程间的同步机制/275
9.1互斥锁275
9.1.1互斥锁基本原理275
9.1.2互斥锁基本操作275
9.1.3互斥锁应用实例278
9.2条件变量279
9.2.1条件变量基本原理279
9.2.2条件变量基本操作279
9.2.3条件变量应用实例281
9.3读写锁284
9.3.1读写锁基本原理284
9.3.2读写锁基本操作284
9.3.3读写锁应用实例287
9.4线程与信号289
9.4.1线程信号管理290
9.4.2线程信号应用实例291
9.5小结295
习题295
第10章网络程序设计/296
10.1网络知识基础296
10.1.1TCP/IP参考模型296
10.1.2Linux中TCP/IP网络的层结构296
10.1.3TCP协议297
10.1.4UDP协议298
10.2套接字299
10.2.1套接字概述299
10.2.2套接字编程接口300
10.2.3套接字通信流程303
10.3套接字基础307
10.3.1套接字地址结构307
10.3.2字节顺序309
10.3.3字节处理函数310
10.4套接字编程311
10.4.1基于TCP协议的网络通信311
10.4.2基于UDP协议的网络通信316
10.5小结319
习题319
附录实验/321
实验1Linux基础知识321
实验2C程序开发工具321
实验3文件I/O操作322
实验4进程管理及守护进程323
实验5重定向和管道编程323
实验6信号安装及处理方式324
实验7System V IPC进程通信325
实验8线程管理325
实验9线程间通信326
实验10套接字编程326
第1章Linux基础知识/1
1.1Linux简介1
1.1.1Linux系统的发展1
1.1.2与Linux相关的一些知识3
1.2Linux系统编程5
1.2.1什么是系统编程5
1.2.2系统编程的学习内容及方法6
1.2.3一个例子7
1.2.4系统调用和库函数10
1.3常用工具及命令10
1.3.1命令格式10
1.3.2常用工具11
1.3.3常用命令15
1.3.4获取帮助17
1.4小结20
习题20
第2章C程序开发工具/22
2.1编辑工具22
2.1.1编辑工具介绍22
2.1.2vi和vim程序编辑器25
2.2gcc编译器34
2.3gdb调试器36
2.3.1启动和退出gdb37
2.3.2显示和查找程序源代码38
2.3.3执行程序和获取帮助39
2.3.4设置和管理断点40
2.3.5查看和设置变量的值45
2.3.6控制程序的执行46
2.4make和Makefile48〖1〗Linux环境高级程序设计目录[3]〖3〗2.4.1make命令48
2.4.2编写Makefile文件50
2.5小结54
习题55
第3章文件及目录管理/56
3.1文件和I/O操作分类56
3.1.1文件概念56
3.1.2文件操作分类56
3.2Linux文件系统概述58
3.2.1文件结构58
3.2.2文件系统模型59
3.2.3目录、索引结点和文件描述符60
3.2.4文件的分类63
3.2.5文件访问权限控制64
3.3文件的读写68
3.3.1文件打开、创建和关闭69
3.3.2文件的读写72
3.3.3文件读写指针的移动80
3.3.4标准I/O的文件流82
3.4文件属性及相关系统调用87
3.4.1获取文件属性87
3.4.2修改文件的访问权限91
3.4.3修改文件的用户属性93
3.4.4获取用户的信息94
3.4.5改变文件大小95
3.4.6获取文件的时间属性96
3.5目录操作97
3.5.1打开目录97
3.5.2读取目录项98
3.5.3关闭目录98
3.6实现自己的ls命令100
3.7小结105
习题106
第4章进程管理/107
4.1Linux可执行程序的存储结构与进程结构107
4.1.1Linux可执行程序的存储结构107
4.1.2Linux系统的进程结构109
4.1.3进程树110
4.2进程的环境和进程属性111
4.2.1进程的环境111
4.2.2进程的状态112
4.2.3进程的基本属性115
4.2.4进程的用户属性121
4.3进程管理124
4.3.1创建进程124
4.3.2在进程中运行新代码127
4.3.3vfork函数131
4.3.4进程退出133
4.3.5wait函数138
4.3.6Shell的实现流程142
4.4Linux中的特殊进程143
4.4.1孤儿进程143
4.4.2僵尸进程144
4.4.3守护进程145
4.4.4出错记录148
4.5小结150
习题150
第5章重定向与管道/151
5.1重定向和管道命令151
5.1.1重定向命令151
5.1.2管道命令152
5.2实现重定向153
5.2.1重定向的实施者153
5.2.2实现重定向的前提条件154
5.2.3dup和dup2154
5.2.4重定向的三种方法157
5.2.5ls l>list.txt159
5.3管道编程161
5.3.1匿名管道161
5.3.2命名管道165
5.3.3ls l|grep root168
5.3.4popen和pclose170
5.4小结173
习题174
第6章信号/175
6.1信号概述175
6.1.1什么是信号175
6.1.2信号的来源和处理过程177
6.1.3信号的处理方式177
6.2早期信号处理函数——signal178
6.2.1signal函数实现信号的三种处理方式178
6.2.2signal函数存在的问题182
6.3信号处理函数——sigaction183
6.3.1sigaction系统调用183
6.3.2sigaction函数参数的说明186
6.4信号其他相关函数190
6.4.1kill与raise190
6.4.2alarm与pause192
6.4.3实现sleep函数193
6.5小结194
习题195
第7章进程间通信/196
7.1选择进程间通信方式196
7.1.1文件实现进程间通信196
7.1.2命名管道实现进程间通信199
7.2共享内存201
7.2.1什么是共享内存201
7.2.2共享内存相关系统调用203
7.2.3共享内存实现进程间通信206
7.2.4三种通信方式的比较208
7.3信号量209
7.3.1信号量及相关系统调用209
7.3.2使用信号量控制对共享内存的访问214
7.3.3信号量机制总结221
7.4System V IPC222
7.4.1Linux中的进程通信机制222
7.4.2System V IPC概述223
7.4.3IPC的标识符和键224
7.5消息队列225
7.5.1消息队列的概念225
7.5.2消息队列相关系统调用226
7.5.3使用消息队列实现进程间通信229
7.6小结232
习题232
第8章线程/233
8.1线程概述233
8.1.1线程的定义233
8.1.2用户级线程和内核级线程234
8.1.3线程与进程的对比234
8.2线程基本操作235
8.2.1线程创建235
8.2.2线程退出/等待238
8.2.3线程终止244
8.2.4线程挂起247
8.2.5线程的分离249
8.2.6线程的一次性初始化251
8.2.7线程的私有数据253
8.3线程属性257
8.3.1线程属性对象258
8.3.2设置/获取线程detachstate属性260
8.3.3设置与获取线程栈相关属性261
8.4线程应用举例266
8.5小结273
习题273
第9章线程间的同步机制/275
9.1互斥锁275
9.1.1互斥锁基本原理275
9.1.2互斥锁基本操作275
9.1.3互斥锁应用实例278
9.2条件变量279
9.2.1条件变量基本原理279
9.2.2条件变量基本操作279
9.2.3条件变量应用实例281
9.3读写锁284
9.3.1读写锁基本原理284
9.3.2读写锁基本操作284
9.3.3读写锁应用实例287
9.4线程与信号289
9.4.1线程信号管理290
9.4.2线程信号应用实例291
9.5小结295
习题295
第10章网络程序设计/296
10.1网络知识基础296
10.1.1TCP/IP参考模型296
10.1.2Linux中TCP/IP网络的层结构296
10.1.3TCP协议297
10.1.4UDP协议298
10.2套接字299
10.2.1套接字概述299
10.2.2套接字编程接口300
10.2.3套接字通信流程303
10.3套接字基础307
10.3.1套接字地址结构307
10.3.2字节顺序309
10.3.3字节处理函数310
10.4套接字编程311
10.4.1基于TCP协议的网络通信311
10.4.2基于UDP协议的网络通信316
10.5小结319
习题319
附录实验/321
实验1Linux基础知识321
实验2C程序开发工具321
实验3文件I/O操作322
实验4进程管理及守护进程323
实验5重定向和管道编程323
实验6信号安装及处理方式324
实验7System V IPC进程通信325
实验8线程管理325
实验9线程间通信326
实验10套接字编程326
前 言
前言
出于安全、稳定等因素的考虑,开源软件受到了各行各业的青睐,其中以Linux操作系统最为突出。作为当前最为流行的操作系统之一——Linux已发展得较为成熟,其良好的稳定性和优异的性能带给各类用户优越的体验。Linux系统的使用范围越来越广,随之而来的是Linux环境下各类应用软件需求的暴增。学习Linux环境编程对提高IT从业者的竞争力和整个软件行业的发展无疑是相当有意义的。
本书以培养Linux系统程序分析能力为目标,以命令程序设计为驱动。在解决问题的过程中始终以培养分析问题的能力为基础,介绍有效使用Linux在线手册的方法,从而找到解决问题的突破口,进一步找到合适的系统调用接口,设计相关的命令程序,最终解决问题。
C语言是广大程序设计人员都已掌握的一门程序设计语言,同时也是实现Linux系统所使用的程序设计语言。本书使用C语言结合Linux API进行程序设计,全书共分为10章,内容如下所述:
第1章Linux基础知识,介绍Linux操作系统的发展情况以及系统编程的概念,同时还介绍Linux系统中的一些常用工具及命令。
第2章C程序开发工具,介绍Linux环境下编写C语言程序所要用到的一些工具,包括vim、gcc、gdb、makefile等。
第3章文件及目录管理,介绍POSIX标准下文件的各类I/O操作以及与流文件的关系和相互转换。
第4章进程管理,介绍在Linux环境中程序和进程的关系、进程的基本属性、一个进程从生到死的全过程,最后介绍Linux系统中的一些特殊进程。
第5章重定向与管道,以实现重定向命令为引入点,重点介绍使用管道实现进程间通信的方法。
第6章信号,介绍信号的几种处理策略和信号在进程间通信中的使用方式。
第7章进程间通信,介绍使用共享内存、信号量和消息队列来实现进程间通信的方法,同时总结Linux环境下进程间通信的所有机制。
第8章线程,介绍线程的基本概念和基本操作。
第9章线程间的同步机制,介绍线程间的通信机制以及线程同步互斥的问题。〖1〗Linux环境高级程序设计前言[3]〖3〗第10章网络程序设计,介绍套接字机制在不同主机的进程间如何实现通信以及在Linux环境下网络套接字编程的基本方法。
其中第5~7章以及第10章都是有关于进程间通信机制的内容。
为检查各章的学习情况,本书最后配备了相应的实验。
作为Linux环境程序设计的入门教材,本书语言简练、论述由浅入深,并辅以大量的示例程序和丰富的图表,使读者能够更好地理解各种抽象的概念和关系,帮助读者理解全书内容。本书面向各类高等院校的计算机相关专业学生以及对开源环境程序设计有兴趣的编程爱好者,要求读者有一定的C语言编程基础。书中除了讲解程序的设计实现之外,着重讲解解决问题的分析过程,力求使读者掌握问题的分析方法,逐步培养读者具有在Linux环境下独立设计和实现应用级甚至系统级程序的能力。
需要说明的一点是,由于系统调用从C语言语法的角度上来看与函数没有区别,因此在本书中不会引起歧义的地方并没有严格区分系统调用和库函数的表述。如果需要明确某一表述是系统调用还是库函数,可以使用man手册查找。man手册的第2章为系统调用,第3章为库函数。
本书主要由黄茹主编,王小银、张丽丽为副主编。其中黄茹编写了第1、4、5、6、7章;王小银编写了第8、9、10章;张丽丽编写了第2、3章;最后由黄茹负责对全书进行了统稿。此外,在本书的编写过程中,感谢陈莉君、舒新峰、刘霞林、王春梅对本书提出了宝贵的意见。
尽管本书经过了编者反复的修改,但由于编者水平和经验有限,时间仓促,书中难免存在错漏之处,殷切希望广大读者批评指正。
出于安全、稳定等因素的考虑,开源软件受到了各行各业的青睐,其中以Linux操作系统最为突出。作为当前最为流行的操作系统之一——Linux已发展得较为成熟,其良好的稳定性和优异的性能带给各类用户优越的体验。Linux系统的使用范围越来越广,随之而来的是Linux环境下各类应用软件需求的暴增。学习Linux环境编程对提高IT从业者的竞争力和整个软件行业的发展无疑是相当有意义的。
本书以培养Linux系统程序分析能力为目标,以命令程序设计为驱动。在解决问题的过程中始终以培养分析问题的能力为基础,介绍有效使用Linux在线手册的方法,从而找到解决问题的突破口,进一步找到合适的系统调用接口,设计相关的命令程序,最终解决问题。
C语言是广大程序设计人员都已掌握的一门程序设计语言,同时也是实现Linux系统所使用的程序设计语言。本书使用C语言结合Linux API进行程序设计,全书共分为10章,内容如下所述:
第1章Linux基础知识,介绍Linux操作系统的发展情况以及系统编程的概念,同时还介绍Linux系统中的一些常用工具及命令。
第2章C程序开发工具,介绍Linux环境下编写C语言程序所要用到的一些工具,包括vim、gcc、gdb、makefile等。
第3章文件及目录管理,介绍POSIX标准下文件的各类I/O操作以及与流文件的关系和相互转换。
第4章进程管理,介绍在Linux环境中程序和进程的关系、进程的基本属性、一个进程从生到死的全过程,最后介绍Linux系统中的一些特殊进程。
第5章重定向与管道,以实现重定向命令为引入点,重点介绍使用管道实现进程间通信的方法。
第6章信号,介绍信号的几种处理策略和信号在进程间通信中的使用方式。
第7章进程间通信,介绍使用共享内存、信号量和消息队列来实现进程间通信的方法,同时总结Linux环境下进程间通信的所有机制。
第8章线程,介绍线程的基本概念和基本操作。
第9章线程间的同步机制,介绍线程间的通信机制以及线程同步互斥的问题。〖1〗Linux环境高级程序设计前言[3]〖3〗第10章网络程序设计,介绍套接字机制在不同主机的进程间如何实现通信以及在Linux环境下网络套接字编程的基本方法。
其中第5~7章以及第10章都是有关于进程间通信机制的内容。
为检查各章的学习情况,本书最后配备了相应的实验。
作为Linux环境程序设计的入门教材,本书语言简练、论述由浅入深,并辅以大量的示例程序和丰富的图表,使读者能够更好地理解各种抽象的概念和关系,帮助读者理解全书内容。本书面向各类高等院校的计算机相关专业学生以及对开源环境程序设计有兴趣的编程爱好者,要求读者有一定的C语言编程基础。书中除了讲解程序的设计实现之外,着重讲解解决问题的分析过程,力求使读者掌握问题的分析方法,逐步培养读者具有在Linux环境下独立设计和实现应用级甚至系统级程序的能力。
需要说明的一点是,由于系统调用从C语言语法的角度上来看与函数没有区别,因此在本书中不会引起歧义的地方并没有严格区分系统调用和库函数的表述。如果需要明确某一表述是系统调用还是库函数,可以使用man手册查找。man手册的第2章为系统调用,第3章为库函数。
本书主要由黄茹主编,王小银、张丽丽为副主编。其中黄茹编写了第1、4、5、6、7章;王小银编写了第8、9、10章;张丽丽编写了第2、3章;最后由黄茹负责对全书进行了统稿。此外,在本书的编写过程中,感谢陈莉君、舒新峰、刘霞林、王春梅对本书提出了宝贵的意见。
尽管本书经过了编者反复的修改,但由于编者水平和经验有限,时间仓促,书中难免存在错漏之处,殷切希望广大读者批评指正。
编者
2019年1月
免费在线读
第5章重定向与管道在Linux系统中,系统会默认为命令或程序打开三个标准I/O文件,保证命令或程序可以与用户进行交互。除此之外,还可以将这三个标准文件重定向,借此可以实现从指定的位置获取输入信息或将输出信息、错误信息保存在指定的文件中。同时,Linux系统还提供管道的功能,可以将多条命令或程序之间的输出和输入相衔接,实现灵活的Shell编程。
5.1重定向和管道命令〖4/5〗5.1.1重定向命令所有的UNIX I/O重定向都是基于标准数据流的原理。在Shell中键入命令运行时,内核将会为命令进程打开三个标准I/O设备文件,并且用文件描述符0、1、2与它们关联,其中文件描述符0对应标准输入设备,1对应标准输出设备,2对应标准错误文件。进程在运行过程中,默认从标准输入设备文件读取数据,将输出数据写入标准输出设备文件,如果出错的话,错误信息将会写入标准错误文件。例如,使用cat命令时,系统将会把结果显示在标准输出设备上。通常标准输入设备文件为键盘,标准输出设备文件和标准错误文件为显示器,如图5.1所示。
图5.1进程和标准设备文件的联系
在某种情况下,用户希望能够将信息输出到某个文件中,而不是显示在标准输出设备上,此时可以将该进程的标准输出进行重定向。重定向命令分为输入重定向、输出重定向和错误重定向。〖1〗Linux环境高级程序设计第5章重定向与管道[3]〖3〗1. 输入重定向
输入重定向使用符号“2. 输出重定向
符号“>”或“>>”都可以用来表示输出重定向,两者的差异在于: 前者以覆盖的方式输出,后者以追加的方式输出。输出重定向命令的格式为: command>file或 command>>file例如,命令cat file1 file2>file3表示将文件file1和file2的内容合并输出到文件file3中,这条命令也可以使用以下两条命令来替换: cat file1>file3
cat file2>>file3如果重定向使用的是符号“>!”,那么表示输出重定向强制覆盖文件原有的内容。
3. 错误重定向
错误重定向可以使用符号“2>”或“2>>”来表示,两个符号的差异与输出重定向类似。错误重定向的具体命令格式为: command 2>file或command 2>>file使用错误重定向后,如果在命令执行的过程中有错误发生,错误信息将会记录在文件file中。
除了以上介绍的重定向符号外,还可以使用“>&”“1>”“2>&1”等符号。使用重定向符号时,也并不仅限于只能在命令末尾处出现重定向符号。这些重定向符号可以单独使用,也可以组合使用,例如: wc result.wc 2>error.txt以上命令表示统计file1文件的字符、单词和行数,将结果记录在result.wc文件中,如果出错,错误信息记录在error.txt文件中。
5.1.2管道命令
如果某些数据必须要经过几次命令操作之后才能得到所想要的结果,此时可以使用管道符号将这些命令连接起来。管道命令会将符号先后的命令连接起来,将前一条命令的输出作为后一条命令的输入。符号“|”称为管道操作符,管道命令的具体格式如下: command1|command2|command3|……例如: grep root /etc/passwd | sort图5.2管道命令示意图
该命令表示在/etc/passwd文件中搜索出含有root的内容,并将这些内容排序。这条管道命令的示意图如图5.2所示。
又如: ls -l|grep hr|lpr该命令表示列出当前目录中包含hr模式串的文件名称,将结果打印出来。
5.2实现重定向〖4/5〗5.2.1重定向的实施者在实现重定向命令之前,需要借由一个示例程序先来明确一下重定向是由待执行的命令或程序还是Shell来实现的。 [示例程序5.1 for_redirect.c]
#include
main(int argc, char argv[ ])
{
int i;
char buf[80];
scanf(“%s”,buf);
printf(“info from file:%s\n”,buf);
printf(“arg list\n”);
for(i=0;i printf(“argv[%d]:%s\n”,i,argv[i]);
fprintf(stderr,”where do you find this?\n”);
}在示例程序5.1中,分别使用scanf、printf和fprintf函数对标准输入、标准输出和标准错误文件进行了读、写操作,并且还使用printf函数输出了执行程序时所附带的参数。当使用两种不同的方式来运行该程序时,请观察一下重定向符号是否会被Shell当作参数传递给用户程序。
将程序编译后按照带重定向和不带重定向两种方式运行,得到结果如下: root@ubuntu:~#./for_redirect para1 para2 para3
Hello
info from file:Hello
arg list
argv[0]:./for_redirect
argv[1]:para1
argv[2]:para2
argv[3]:para3
where do you find this?
root@ubuntu:~#./for_redirect para1 para2 para3>result 2>err.txt
Hello
root@ubuntu:~#cat result
info from file:Hello
arg list
argv[0]:./for_redirect
argv[1]:para1
argv[2]:para2
argv[3]:para3
root@ubuntu:~#cat err.txt
where do you find this?从程序两次运行的输出结果来看,输出的参数中并没有重定向符号和文件名称,因此可以确定: 重定向并不是由命令或用户程序实现的,而是由Shell实现的。因此可以明确: 要实现重定向命令,就需要改写第4章所编写的简单Shell程序,在Shell执行命令之前添加实现重定向的功能。
5.2.2实现重定向的前提条件
当着手设计带有重定向功能的Shell程序时,还要谨记: 之所以能够实现重定向,是因为Linux环境具有以下几个特性:
(1) 系统为每个进程所打开的标准I/O设备文件对应着值最小的三个文件描述符0、1、2。实际上一个进程打开的所有文件的信息是储存在一个结构体数组之中的,而打开文件对应的文件描述符就是该文件信息在结构体数组中的存储位置,即数组的下标。
(2) 当进程使用open、dup等文件操作时,新分配的文件描述符遵循最低可用文件描述符原则。即当打开一个文件时,系统为此文件安排的文件描述符总是可用的文件描述符中值最小的那一个。
(3) 在一个进程中,如果在文件打开操作以后使用了eXec族函数,那么eXec函数将不会影响执行前打开的文件描述符集合。
5.2.3dup和dup2
dup和dup2是在Linux中实现重定向命令时经常会使用到的两个函数。两者都可用于复制文件描述符,dup函数的接口规范说明如表5.1所示。续表表5.1dup函数的接口规范说明
函数名称dup函数功能复制一个文件描述符头文件#include函数原型int dup(int oldfd);参数oldfd: 被复制的文件描述符返回值>-1: 复制成功,返回新的文件描述符
-1: 出错dup函数用来复制一个文件描述符,参数oldfd指向一个打开的文件,函数的返回值返回复制后的新文件描述符,新的文件描述符也指向oldfd所指向的文件列表项,如图5.3所示,如果该进程没有打开其他文件,那么在执行了dup(0)之后,文件描述符3将会指向0所对应的文件。
图5.3dup(0) 后文件描述符的关系
dup2也可用于复制文件描述符,它与dup的不同之处在于,dup2可以指定要把信息复制给哪一个文件描述符。dup2函数的接口规范说明如表5.2所示。表5.2dup2函数的接口规范说明
函数名称dup2函数功能复制一个文件描述符头文件#include函数原型int dup2(int oldfd, int newfd);参数oldfd: 被复制的文件描述符
newfd: 新的文件描述符返回值> -1: 复制成功,返回新的文件描述符
-1: 出错说明: dup2在复制文件描述符时,如果newfd已分配给某个打开的文件,那么系统会先关闭newfd,切断与原先文件的联系,然后再进行复制。
示例程序5.2说明了如何使用dup和dup2来复制文件描述符。 [示例程序5.2 exp_dup.c]
#include
#include
#include
#include
main()
{
int fd,fd1,fd2;
char buf[10];
fd=open(“text.txt”,O_RDONLY);
if(fd<0)
{
perror(“open”);
exit(EXIT_FAILURE);
}
fd1=dup(fd);
if(fd1<0)
{
perror(“dup”);
exit(EXIT_FAILURE);
}
fd2=dup2(fd,5);
if(fd2<0)
{
perror(“dup2”);
exit(EXIT_FAILURE);
}
if(read(fd,buf,10)>0)
write(STDOUT_FILENO,buf,10);
if(read(fd1,buf,10)>0)
write(STDOUT_FILENO,buf,10);
if(read(fd2,buf,10)>0)
write(STDOUT_FILENO,buf,10);
close(fd);
close(fd1);
close(fd2);
}编译后运行,得到程序的运行结果如下: root@ubuntu:~#cat text.txt
This is a test text,
Can you see my greeting?
root@ubuntu:~#./exp_dup
This is a test text,
Can you s从运行结果可以看出,fd通过open函数分配给了文件text.txt,文件描述符fd1和fd2都复制了fd,因此三个文件描述符指向同一个文件。尽管fd、fd1和fd2是三个不同的文件描述符,但它们使用的是同一个打开的文件表项(struct file)。因此不管通过哪个文件描述符改变了文件读写指针的位置,都会对其他两个文件描述符造成影响。从这个示例的运行结果也能够看到,输出结果并不是三次从头开始的10个字符,而是从头开始连续的30个字符。
5.2.4重定向的三种方法
通过上一节的学习,可以分析出实现重定向的流程: 0、1、2三个文件描述符原先是与标准I/O设备文件关联的,需要重定向时,可以使用open、dup或dup2等函数将文件描述符0、1或2与指定的重定向文件相关联,这样就可以实现标准I/O设备文件的重定向。在Linux系统中,可以使用三种方法来实现重定向功能,分别是:
close then open: 关闭指定的标准I/O设备文件,打开重定向文件。
open close dup close: 打开重定向文件,关闭指定的标准I/O设备文件,复制重定向文件的文件描述符,关闭第一步打开的文件描述符。
open dup2 close: 打开重定向文件,将指定的标准I/O设备文件描述符作为参数,复制重定向文件的文件描述符,关闭第一步打开的文件描述符。
针对以上三种重定向的方法,我们来一一讲解。
1. close then open
在这种方法中,程序调用close函数关闭指定的文件描述符与标准设备文件的联系,此时该文件描述符就处于空闲可分配状态;随后使用open函数打开指定的重定向文件,由于open遵循最低可用文件描述符原则,打开的文件将获得第一步操作释放出来的文件描述符。示例程序5.3实现了将标准输入重定向: [示例程序5.3 exp_redirect1.c]
#include
#include
#include
main()
{
int fd;
char buf[80];
close(0);
if((fd=open(“./text.txt”,O_RDONLY))!=0)
{
perror(“open”);
exit(EXIT_FAILURE);
}
read(0,buf,80);
write(1,buf,80);
}该程序将标准输入重定向到当前目录中的text.txt文件,从中读取80个字节的数据,输出在标准输出设备上。文件描述符0关联的文件变化情况如图5.4所示。
图5.4close then open方法的示意图
2. open close dup close
这种方法首先使用open函数打开指定的重定向文件,获取该文件的文件描述符fd;随后,使用close函数关闭标准I/O文件,释放其对应的文件描述符;之后使用dup函数复制fd,此时由于dup函数遵循最低可用文件描述符原则,因此将会把fd复制给第二步中close释放出来的文件描述符;最后使用close关闭fd即可。示例程序5.4使用第二种方法来实现重定向,其功能和示例程序5.3一样,将输入重定向到当前目录中的text.txt文件上。 [示例程序5.4 exp_redirect2.c]
main()
{
int fd,newfd;
char buf[80];
fd=open(“./text.txt “,O_RDONLY);
close(0);
newfd=dup(fd);
if(newfd!=0)
{
perror(“dup”);
exit(EXIT_FAILURE);
}
close(fd);
read(0,buf,80);
write(1,buf,80);
}在这个示例程序中,文件描述符关联文件的变化情况如图5.5所示。
图5.5open close dup close的过程
3. open dup2 close
这种方法与open close dup close方法类似,不同之处在于,使用dup2将open close dup close方法的第二步和第三步合而为一,直接关闭了标准I/O文件并进行了文件描述符的复制。示例程序5.5为使用方法三实现输入重定向的代码。 [示例程序5.5 exp_redirect3.c]
main()
{
int fd,newfd;
char buf[80];
fd=open(“./text.txt “,O_RDONLY);
newfd=dup2(fd,0);
if(newfd!=0)
{
perror(“dup2”);
exit(EXIT_FAILURE);
}
close(fd);
read(0,buf,80);
write(1,buf,80);
}以上三种方法均可以实现重定向操作,具体使用哪种方法可以根据实际情况来决定。如果已知重定向文件的文件名,那么三种方法都可以实现;但如果在程序运行过程中只能获取重定向文件的文件描述符,那么就要考虑使用后面两种方法。
5.2.5ls l>list.txt
为了使读者能够更好地掌握如何在简单Shell中实现重定向功能,我们先来学习一个具体的重定向命令如何实现: ls -l>list.txt首先,先来分析一下Shell在实现ls l命令时的过程是怎样的。从第4章了解到当Shell执行一条前台命令时,将会为命令创建一个子进程;随后在进程中使用eXec函数来执行命令程序,此时Shell进程处于阻塞状态等待命令进程结束;当命令进程结束后,Shell将会显示命令进程运行的结果。从这个过程中可以发现:
(1) 命令进程需要进行重定向,但是Shell进程并不需要重定向。
(2) 为了执行命令,Shell需要调用fork函数创建子进程,子进程需要调用eXec函数来执行ls l命令。这就要求必须在创建了子进程之后,调用eXec函数之前将子进程的输出重定向到文件list.txt。这样一来,重定向仅仅是针对子进程的,并且子进程在调用eXec函数时,并不会影响到在eXec函数执行前已打开的文件描述符列表。
(3) 由于list.txt有可能是一个不存在的文件,还需要将重定向的三种方法中的open函数换成creat函数。
程序代码如示例程序5.6所示。 [示例程序5.6 exp_lsre.c]
main()
{
int pid,fd;
printf(“This is to show how to redirect!\n”);
if((pid=fork())==-1)
{
perror(“fork”);
exit(EXIT_FAILURE);
}
else if(pid==0)
{
close(1);
fd=creat(“list.txt”,0644);
if(execlp(“ls”,”ls”,”-l”,NULL)<0)
{
perror(“exec”);
exit(EXIT_FAILURE);
}
}
else if(pid!=0)
{
wait(NULL);
system(“cat list.txt”);
}
}示例程序5.6使用了close then open来实现重定向。请读者思考一下,命令ls l>>list.txt、ls l 2>err.txt应该如何实现?
从示例程序5.6中,可以分析出在简单Shell程序中实现重定向的流程如下:
(1) 以字符串的方式读入命令后,将命令字符串分解为命令名称、参数、选项、重定向等各个分项。
(2) 如果存在重定向符号,则需要根据重定向符号的类型,分解出重定向文件名和需要重定向的标准I/O文件。
(3) 调用fork创建子进程,在子进程中按照重定向的要求,重定向标准I/O文件,父进程调用wait等待子进程结束。
5.1重定向和管道命令〖4/5〗5.1.1重定向命令所有的UNIX I/O重定向都是基于标准数据流的原理。在Shell中键入命令运行时,内核将会为命令进程打开三个标准I/O设备文件,并且用文件描述符0、1、2与它们关联,其中文件描述符0对应标准输入设备,1对应标准输出设备,2对应标准错误文件。进程在运行过程中,默认从标准输入设备文件读取数据,将输出数据写入标准输出设备文件,如果出错的话,错误信息将会写入标准错误文件。例如,使用cat命令时,系统将会把结果显示在标准输出设备上。通常标准输入设备文件为键盘,标准输出设备文件和标准错误文件为显示器,如图5.1所示。
图5.1进程和标准设备文件的联系
在某种情况下,用户希望能够将信息输出到某个文件中,而不是显示在标准输出设备上,此时可以将该进程的标准输出进行重定向。重定向命令分为输入重定向、输出重定向和错误重定向。〖1〗Linux环境高级程序设计第5章重定向与管道[3]〖3〗1. 输入重定向
输入重定向使用符号“2. 输出重定向
符号“>”或“>>”都可以用来表示输出重定向,两者的差异在于: 前者以覆盖的方式输出,后者以追加的方式输出。输出重定向命令的格式为: command>file或 command>>file例如,命令cat file1 file2>file3表示将文件file1和file2的内容合并输出到文件file3中,这条命令也可以使用以下两条命令来替换: cat file1>file3
cat file2>>file3如果重定向使用的是符号“>!”,那么表示输出重定向强制覆盖文件原有的内容。
3. 错误重定向
错误重定向可以使用符号“2>”或“2>>”来表示,两个符号的差异与输出重定向类似。错误重定向的具体命令格式为: command 2>file或command 2>>file使用错误重定向后,如果在命令执行的过程中有错误发生,错误信息将会记录在文件file中。
除了以上介绍的重定向符号外,还可以使用“>&”“1>”“2>&1”等符号。使用重定向符号时,也并不仅限于只能在命令末尾处出现重定向符号。这些重定向符号可以单独使用,也可以组合使用,例如: wc result.wc 2>error.txt以上命令表示统计file1文件的字符、单词和行数,将结果记录在result.wc文件中,如果出错,错误信息记录在error.txt文件中。
5.1.2管道命令
如果某些数据必须要经过几次命令操作之后才能得到所想要的结果,此时可以使用管道符号将这些命令连接起来。管道命令会将符号先后的命令连接起来,将前一条命令的输出作为后一条命令的输入。符号“|”称为管道操作符,管道命令的具体格式如下: command1|command2|command3|……例如: grep root /etc/passwd | sort图5.2管道命令示意图
该命令表示在/etc/passwd文件中搜索出含有root的内容,并将这些内容排序。这条管道命令的示意图如图5.2所示。
又如: ls -l|grep hr|lpr该命令表示列出当前目录中包含hr模式串的文件名称,将结果打印出来。
5.2实现重定向〖4/5〗5.2.1重定向的实施者在实现重定向命令之前,需要借由一个示例程序先来明确一下重定向是由待执行的命令或程序还是Shell来实现的。 [示例程序5.1 for_redirect.c]
#include
main(int argc, char argv[ ])
{
int i;
char buf[80];
scanf(“%s”,buf);
printf(“info from file:%s\n”,buf);
printf(“arg list\n”);
for(i=0;i printf(“argv[%d]:%s\n”,i,argv[i]);
fprintf(stderr,”where do you find this?\n”);
}在示例程序5.1中,分别使用scanf、printf和fprintf函数对标准输入、标准输出和标准错误文件进行了读、写操作,并且还使用printf函数输出了执行程序时所附带的参数。当使用两种不同的方式来运行该程序时,请观察一下重定向符号是否会被Shell当作参数传递给用户程序。
将程序编译后按照带重定向和不带重定向两种方式运行,得到结果如下: root@ubuntu:~#./for_redirect para1 para2 para3
Hello
info from file:Hello
arg list
argv[0]:./for_redirect
argv[1]:para1
argv[2]:para2
argv[3]:para3
where do you find this?
root@ubuntu:~#./for_redirect para1 para2 para3>result 2>err.txt
Hello
root@ubuntu:~#cat result
info from file:Hello
arg list
argv[0]:./for_redirect
argv[1]:para1
argv[2]:para2
argv[3]:para3
root@ubuntu:~#cat err.txt
where do you find this?从程序两次运行的输出结果来看,输出的参数中并没有重定向符号和文件名称,因此可以确定: 重定向并不是由命令或用户程序实现的,而是由Shell实现的。因此可以明确: 要实现重定向命令,就需要改写第4章所编写的简单Shell程序,在Shell执行命令之前添加实现重定向的功能。
5.2.2实现重定向的前提条件
当着手设计带有重定向功能的Shell程序时,还要谨记: 之所以能够实现重定向,是因为Linux环境具有以下几个特性:
(1) 系统为每个进程所打开的标准I/O设备文件对应着值最小的三个文件描述符0、1、2。实际上一个进程打开的所有文件的信息是储存在一个结构体数组之中的,而打开文件对应的文件描述符就是该文件信息在结构体数组中的存储位置,即数组的下标。
(2) 当进程使用open、dup等文件操作时,新分配的文件描述符遵循最低可用文件描述符原则。即当打开一个文件时,系统为此文件安排的文件描述符总是可用的文件描述符中值最小的那一个。
(3) 在一个进程中,如果在文件打开操作以后使用了eXec族函数,那么eXec函数将不会影响执行前打开的文件描述符集合。
5.2.3dup和dup2
dup和dup2是在Linux中实现重定向命令时经常会使用到的两个函数。两者都可用于复制文件描述符,dup函数的接口规范说明如表5.1所示。续表表5.1dup函数的接口规范说明
函数名称dup函数功能复制一个文件描述符头文件#include函数原型int dup(int oldfd);参数oldfd: 被复制的文件描述符返回值>-1: 复制成功,返回新的文件描述符
-1: 出错dup函数用来复制一个文件描述符,参数oldfd指向一个打开的文件,函数的返回值返回复制后的新文件描述符,新的文件描述符也指向oldfd所指向的文件列表项,如图5.3所示,如果该进程没有打开其他文件,那么在执行了dup(0)之后,文件描述符3将会指向0所对应的文件。
图5.3dup(0) 后文件描述符的关系
dup2也可用于复制文件描述符,它与dup的不同之处在于,dup2可以指定要把信息复制给哪一个文件描述符。dup2函数的接口规范说明如表5.2所示。表5.2dup2函数的接口规范说明
函数名称dup2函数功能复制一个文件描述符头文件#include函数原型int dup2(int oldfd, int newfd);参数oldfd: 被复制的文件描述符
newfd: 新的文件描述符返回值> -1: 复制成功,返回新的文件描述符
-1: 出错说明: dup2在复制文件描述符时,如果newfd已分配给某个打开的文件,那么系统会先关闭newfd,切断与原先文件的联系,然后再进行复制。
示例程序5.2说明了如何使用dup和dup2来复制文件描述符。 [示例程序5.2 exp_dup.c]
#include
#include
#include
#include
main()
{
int fd,fd1,fd2;
char buf[10];
fd=open(“text.txt”,O_RDONLY);
if(fd<0)
{
perror(“open”);
exit(EXIT_FAILURE);
}
fd1=dup(fd);
if(fd1<0)
{
perror(“dup”);
exit(EXIT_FAILURE);
}
fd2=dup2(fd,5);
if(fd2<0)
{
perror(“dup2”);
exit(EXIT_FAILURE);
}
if(read(fd,buf,10)>0)
write(STDOUT_FILENO,buf,10);
if(read(fd1,buf,10)>0)
write(STDOUT_FILENO,buf,10);
if(read(fd2,buf,10)>0)
write(STDOUT_FILENO,buf,10);
close(fd);
close(fd1);
close(fd2);
}编译后运行,得到程序的运行结果如下: root@ubuntu:~#cat text.txt
This is a test text,
Can you see my greeting?
root@ubuntu:~#./exp_dup
This is a test text,
Can you s从运行结果可以看出,fd通过open函数分配给了文件text.txt,文件描述符fd1和fd2都复制了fd,因此三个文件描述符指向同一个文件。尽管fd、fd1和fd2是三个不同的文件描述符,但它们使用的是同一个打开的文件表项(struct file)。因此不管通过哪个文件描述符改变了文件读写指针的位置,都会对其他两个文件描述符造成影响。从这个示例的运行结果也能够看到,输出结果并不是三次从头开始的10个字符,而是从头开始连续的30个字符。
5.2.4重定向的三种方法
通过上一节的学习,可以分析出实现重定向的流程: 0、1、2三个文件描述符原先是与标准I/O设备文件关联的,需要重定向时,可以使用open、dup或dup2等函数将文件描述符0、1或2与指定的重定向文件相关联,这样就可以实现标准I/O设备文件的重定向。在Linux系统中,可以使用三种方法来实现重定向功能,分别是:
close then open: 关闭指定的标准I/O设备文件,打开重定向文件。
open close dup close: 打开重定向文件,关闭指定的标准I/O设备文件,复制重定向文件的文件描述符,关闭第一步打开的文件描述符。
open dup2 close: 打开重定向文件,将指定的标准I/O设备文件描述符作为参数,复制重定向文件的文件描述符,关闭第一步打开的文件描述符。
针对以上三种重定向的方法,我们来一一讲解。
1. close then open
在这种方法中,程序调用close函数关闭指定的文件描述符与标准设备文件的联系,此时该文件描述符就处于空闲可分配状态;随后使用open函数打开指定的重定向文件,由于open遵循最低可用文件描述符原则,打开的文件将获得第一步操作释放出来的文件描述符。示例程序5.3实现了将标准输入重定向: [示例程序5.3 exp_redirect1.c]
#include
#include
#include
main()
{
int fd;
char buf[80];
close(0);
if((fd=open(“./text.txt”,O_RDONLY))!=0)
{
perror(“open”);
exit(EXIT_FAILURE);
}
read(0,buf,80);
write(1,buf,80);
}该程序将标准输入重定向到当前目录中的text.txt文件,从中读取80个字节的数据,输出在标准输出设备上。文件描述符0关联的文件变化情况如图5.4所示。
图5.4close then open方法的示意图
2. open close dup close
这种方法首先使用open函数打开指定的重定向文件,获取该文件的文件描述符fd;随后,使用close函数关闭标准I/O文件,释放其对应的文件描述符;之后使用dup函数复制fd,此时由于dup函数遵循最低可用文件描述符原则,因此将会把fd复制给第二步中close释放出来的文件描述符;最后使用close关闭fd即可。示例程序5.4使用第二种方法来实现重定向,其功能和示例程序5.3一样,将输入重定向到当前目录中的text.txt文件上。 [示例程序5.4 exp_redirect2.c]
main()
{
int fd,newfd;
char buf[80];
fd=open(“./text.txt “,O_RDONLY);
close(0);
newfd=dup(fd);
if(newfd!=0)
{
perror(“dup”);
exit(EXIT_FAILURE);
}
close(fd);
read(0,buf,80);
write(1,buf,80);
}在这个示例程序中,文件描述符关联文件的变化情况如图5.5所示。
图5.5open close dup close的过程
3. open dup2 close
这种方法与open close dup close方法类似,不同之处在于,使用dup2将open close dup close方法的第二步和第三步合而为一,直接关闭了标准I/O文件并进行了文件描述符的复制。示例程序5.5为使用方法三实现输入重定向的代码。 [示例程序5.5 exp_redirect3.c]
main()
{
int fd,newfd;
char buf[80];
fd=open(“./text.txt “,O_RDONLY);
newfd=dup2(fd,0);
if(newfd!=0)
{
perror(“dup2”);
exit(EXIT_FAILURE);
}
close(fd);
read(0,buf,80);
write(1,buf,80);
}以上三种方法均可以实现重定向操作,具体使用哪种方法可以根据实际情况来决定。如果已知重定向文件的文件名,那么三种方法都可以实现;但如果在程序运行过程中只能获取重定向文件的文件描述符,那么就要考虑使用后面两种方法。
5.2.5ls l>list.txt
为了使读者能够更好地掌握如何在简单Shell中实现重定向功能,我们先来学习一个具体的重定向命令如何实现: ls -l>list.txt首先,先来分析一下Shell在实现ls l命令时的过程是怎样的。从第4章了解到当Shell执行一条前台命令时,将会为命令创建一个子进程;随后在进程中使用eXec函数来执行命令程序,此时Shell进程处于阻塞状态等待命令进程结束;当命令进程结束后,Shell将会显示命令进程运行的结果。从这个过程中可以发现:
(1) 命令进程需要进行重定向,但是Shell进程并不需要重定向。
(2) 为了执行命令,Shell需要调用fork函数创建子进程,子进程需要调用eXec函数来执行ls l命令。这就要求必须在创建了子进程之后,调用eXec函数之前将子进程的输出重定向到文件list.txt。这样一来,重定向仅仅是针对子进程的,并且子进程在调用eXec函数时,并不会影响到在eXec函数执行前已打开的文件描述符列表。
(3) 由于list.txt有可能是一个不存在的文件,还需要将重定向的三种方法中的open函数换成creat函数。
程序代码如示例程序5.6所示。 [示例程序5.6 exp_lsre.c]
main()
{
int pid,fd;
printf(“This is to show how to redirect!\n”);
if((pid=fork())==-1)
{
perror(“fork”);
exit(EXIT_FAILURE);
}
else if(pid==0)
{
close(1);
fd=creat(“list.txt”,0644);
if(execlp(“ls”,”ls”,”-l”,NULL)<0)
{
perror(“exec”);
exit(EXIT_FAILURE);
}
}
else if(pid!=0)
{
wait(NULL);
system(“cat list.txt”);
}
}示例程序5.6使用了close then open来实现重定向。请读者思考一下,命令ls l>>list.txt、ls l 2>err.txt应该如何实现?
从示例程序5.6中,可以分析出在简单Shell程序中实现重定向的流程如下:
(1) 以字符串的方式读入命令后,将命令字符串分解为命令名称、参数、选项、重定向等各个分项。
(2) 如果存在重定向符号,则需要根据重定向符号的类型,分解出重定向文件名和需要重定向的标准I/O文件。
(3) 调用fork创建子进程,在子进程中按照重定向的要求,重定向标准I/O文件,父进程调用wait等待子进程结束。
评论
还没有评论。