Linux操作系统 吴翔虎.

Slides:



Advertisements
Similar presentations
阻塞操作. 在 linux 里,一个等待队列由一个 wait_queue_head_t 类型的结构来描述 等待队列的初始化: static wait_queue_head_t testqueue; init_waitqueue_head(&testqueue);
Advertisements

Linux 环境及 Shell 程序 操作系统实验 1. 二、 Shell 编程与进程通信 常用 shell 命令 文件及文件属性操作 ls 、 cp 、 mv 、 rm ln 、 ln –s 、 chmod 、 groupadd 、 useradd 输入输出操作 echo 、 cat >> 、
LSF系统介绍 张焕杰 中国科学技术大学网络信息中心
第十章 UNIX系统内核结构 10.1 UNIX系统概述 10.2 进程的描述和控制 10.3 进程的同步与通信 10.4 存储器管理
行程(process).
第七章 操作系统接口 7.1 联机用户接口 7.2 Shell命令语言 7.3 系统调用 7.4 UNIX系统调用 7.5 图形用户接口.
基于操作系统的编程复习 张玉宏
檔案及目錄.
Oracle数据库 Oracle 子程序.
第 5 章 文件I/O操作.
在PHP和MYSQL中实现完美的中文显示
第三讲 shell 程序设计.
Kvm异步缺页中断 浙江大学计算机体系结构实验室 徐浩.
LSF系统介绍 张焕杰 中国科学技术大学网络信息中心
台灣大學計算機及資訊網路中心 教學研究組 張傑生
chapter 1-Introduction
Hadoop I/O By ShiChaojie.
第7章:文件共享 与远程控制——回顾 第8章:bash脚本编程 本章教学目标: 了解shell程序的基本结构 网络文件系统NFS的概念
Shell Script 程式設計.
第五章 shell 编程 shell 编程的基本过程分为三步: 1. 建立 shell 文件 包含任意多行操作系统命令或shell命令的文本
Linux 基础与常用命令简介 生物信息学培训班 杭州,2018年1月18日 周银聪.
第7章 Linux环境编程.
多进程编程.
进程及进程管理 第4章 进程及进程管理.
实践演练 广州创龙电子科技有限公司 01 广州创龙电子科技有限公司
网络常用常用命令 课件制作人:谢希仁.
中国科学技术大学计算机系 陈香兰(0512- ) Autumn 2010
进程操作.
第一单元 初识C程序与C程序开发平台搭建 ---观其大略
文件读写实践 广州创龙电子科技有限公司 01 广州创龙电子科技有限公司
第十章 IDL访问数据库 10.1 数据库与数据库访问 1、数据库 数据库中数据的组织由低到高分为四级:字段、记录、表、数据库四种。
作業系統實習課(四) -檔案管理- 實驗室:720A 助教:鄧執中.
如何生成设备节点 广州创龙电子科技有限公司
Linux 文件操作——系统调用和标准 IO 库
第四章 附件 (应用程序软件包).
实验四 Linux文件目录 一、实验目的 1、了解Linux文件系统与目录操作; 2、了解Linux文件系统目录结构;
用event class 从input的root文件中,由DmpDataBuffer::ReadObject读取数据的问题
实验一、进程控制 一、实验目的 1、加深对进程的理解,进一步认识并发执行的实质; 2、分析进程争用资源现象,学习解决进程互斥的方法;
宁波市高校慕课联盟课程 与 进行交互 Linux 系统管理.
宁波市高校慕课联盟课程 与 进行交互 Linux 系统管理.
实验二、线程同步与通信 一、实验目的 1、掌握Linux下线程的概念; 2、了解Linux线程同步与通信的主要机制;
第六章 shell 程序调试 一. 程序执行状态跟踪 程序: -n 读取命令, 但不执行. 主要用于跟踪程序流程是
第二章 登录UNIX操作系统.
C语言程序设计 主讲教师:陆幼利.
如何有效率的學習Linux 培養組合能力 多用程式, 少寫程式 從錯誤訊息中學習 養成略讀 “各種文件” 加強英文基本閱讀能力 勤作筆記
C语言概述 第一章.
本节内容 随机读取 视频提供:昆山爱达人信息技术有限公司.
作業系統 第三章 作業系統結構.
姚金宇 MIT SCHEME 使用说明 姚金宇
杨振伟 清华大学 第一讲:Linux环境下编程(1)
计算机网络与网页制作 Chapter 07:Dreamweaver CS5入门
<编程达人入门课程> 本节内容 内存的使用 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群: ,
信号量(Semaphore).
第4章 Excel电子表格制作软件 4.4 函数(一).
iSIGHT 基本培训 使用 Excel的栅栏问题
本节内容 文件系统 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
多层循环 Private Sub Command1_Click() Dim i As Integer, j As Integer
第 7 章 进程间的通信.
Python 环境搭建 基于Anaconda和VSCode.
实验二:添加Linux系统调用及熟悉常见系统调用
Google的云计算 分布式锁服务Chubby.
2017 Operating Systems 作業系統實習 助教:陳主恩、林欣穎 實驗室:720A Lab4.
基于列存储的RDF数据管理 朱敏
C++语言程序设计 C++语言程序设计 第一章 C++语言概述 第十一组 C++语言程序设计.
杨振伟 清华大学 第一讲:Linux环境下编程(1)
第四章 UNIX文件系统.
实验三 Linux文件目录操作 一、目的 二、要求 了解并掌握Linux文件目录结构。 了解Linux文件系统与目录操作:
《操作系统设计与实现》 第5章 文件系统.
《操作系统设计与实现》 Linux系统编程.
Presentation transcript:

Linux操作系统 吴翔虎

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

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

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

Linux的主要特点 开放性:指系统遵循世界标准规范,特别是遵循开放系统互连(OSI)国际标准。 多用户:是指系统资源可以被不同用户使用,每个用户对自己的资源(例如:文件、设备)有特定的权限,互不影响。 多任务:它是指计算机同时执行多个程序,而且各个程序的运行互相独立。 良好的用户界面 :Linux向用户提供了两种界面:用户界面和系统调用。Linux还为用户提供了图形用户界面。它利用鼠标、菜单、窗口、滚动条等设施,给用户呈现一个直观、易操作、交互性强的友好的图形化界面。

Linux的主要特点 设备独立性:是指操作系统把所有外部设备统一当作成文件来看待,只要安装它们的驱动程序,任何用户都可以象使用文件一样,操纵、使用这些设备,而不必知道它们的具体存在形式。 Linux是具有设备独立性的操作系统,它的内核具有高度适应能力 提供了丰富的网络功能:完善的内置网络是Linux一大特点。 可靠的安全系统:Linux采取了许多安全技术措施,包括对读、写控制、带保护的子系统、审计跟踪、核心授权等,这为网络多用户环境中的用户提供了必要的安全保障。 良好的可移植性:是指将操作系统从一个平台转移到另一个平台使它仍然能按其自身的方式运行的能力。Linux是一种可移植的操作系统,能够在从微型计算机到大型计算机的任何环境中和任何平台上运行。

Linux的组成 LINUX的内核:内核是系统的核心,是运行程序和管理 像磁盘和打印机等硬件设备的核心程序。 LINUX SHELL: Shell是系统的用户界面,提供了用户与内核进行交互操作的一种接口。 LINUX文件系统: Linux文件系统是文件存放在磁盘等存储设备上的组织方法。Linux能支持多种目前浒的文件系统,如EXT2、EXT3、FAT、VFAT、ISO9660、NFS、SMB等。 LINUX应用系统:标准的Linux系统都有一整套称为应用程序的程序集,包括文本编辑器、编程语言、X Window、办公套件、Internet工具、数据库等。

Linux的主要版本 红旗Linux 冲浪Linux 中软Linux Red Hat Linux Mandrake Linux TurboLinux

Linux的系统结构

Linux文件系统

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

处理环境 1、程序与进程 2、子进程与父进程 一个进程可通过系统调用fork创建一个新进程,称创建的进程为子进程,而创建它的进程称为父进程。 程序是一个可执行文件, 进程是一个执行中的程序的实例; 2、子进程与父进程 一个进程可通过系统调用fork创建一个新进程,称创建的进程为子进程,而创建它的进程称为父进程。 3、系统调用的执行—shell命令解释程序 shell允许三种类型的命令: 可执行文件; 包含一系列shell命令的可执行文件; 一个内部shell命令。

Linux操作系统的服务 通过允许进程的创建、终止、挂起及通信来控制进程的执行 对进程在CPU上的执行进行公平调度 对正在执行的进程分配主存 为实现用户数据的有效存储和检索而分配二级存储 允许进程对I/O设备进行有控制的存取

Linux对硬件的要求 1、用户进程的执行状态:用户态和核心态 2、中断与异常 3、存储管理 中断:允许I/O外设或系统时钟异步终端CPU 例外:指的是有一个进程引起的非期望事件,如:非法存储寻找,执行特权指令等 3、存储管理 核心永远驻留在主存中 编译程序产生的是虚地址,核心与机器硬件一起协作,建立虚地址到物理地址的转换

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

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 外壳的增强版。

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的增强版。

bash shell bash 是大多数L i n u x系统的缺省外壳。它克服了Bourne 外壳的缺点,又和Bourne 外壳完全兼容。ba s h有以下的特点: 补全命令行。当你在bash 命令提示符下输入命令或程序名时,你不必输全命令或程序名,按Tab 键,b a s h将自动补全命令或程序名。 通配符。在b a s h下可以使用通配符* 和?。*可以替代多个字符,而?则替代一个字符。

bash shell 历史命令。bash 能自动跟踪你每次输入的命令,并把输入的命令保存在历史列表缓冲区。缓冲区的大小由HISTSIZE 变量控制。当你每次登录后,home 目录下的.bash_history 文件将初始化你的历史列表缓冲区。你也能通过history 和fc 命令执行、编辑历史命令。 别名。在b a s h下,可用alias 和unalias 命令给命令或可执行程序起别名和清除别名。这样你可以用自己习惯的方式输入命令。

bash shell 输入/输出重定向。输入重定向用于改变命令的输入,输出重定向用于改变命令的输出。输出重定向更为常用,它经常用于将命令的结果输入到文件中,而不是屏幕上。输入重定向的命令是<,输出重定向的命令是>。 管道。管道用于将一系列的命令连接起来。也就是把前面的命令的输出作为后面的命令的输入。管道的命令是|。

bash shell 提示符。bash 有两级提示符。第一级提示符就是你登录外壳时见到的,缺省为$。你可以通过重新给p s 1变量赋值来改变第一级提示符。当b a s h需要进一步提示以便补全命令时,会显示第二级提示符。第二级提示符缺省为>,你可以通过重新给p s 2变量赋值来改变第二级提示符。一些特殊意义的字符也可以加入提示符赋值中。 作业控制。作业控制是指在一个作业执行过程中,控制执行的状态。你可以挂起一个正在执行的进程,并在以后恢复该进程的执行。按下Ctrl+Z 挂起正在执行的进程,用b g命令使进程恢复在后台执行,用f g命令使进程恢复在前台执行。

登陆和退出 Linux 启动后,给出login 命令,等待用户登录。 Login: <输入用户名> Password: <输入密码> 如果是正确的用户名和密码,那么你就会进入Linux 的shell, shell给出命令提示符,等待你输入命令(不要随意以r o o t身份登录,以避免对系统造成意外的破坏)。 使用l o g o u t命令退出外壳。

更改账号密码 语法: p a s s w d Old password: <输入旧密码> New password: <输入新密码> Retype new password: <再输入一次密码>

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

远程登录 语法:rlogin 主机名[-1 用户名] 例如: rlogin doc 远程登录到工作站doc 中。 rlogin doc -l user 使用user 帐号登录到工作站doc 中。 语法:telnet 主机名或telnet IP地址 telnet doc telnet 140.109.20.251

文件或目录处理 列出文件或目录下的文件名。 语法: ls [-atFlgR] [name] name :文件名或目录名。 例如: ls -t 依照文件最后修改时间的顺序列出文件名。

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

改变工作目录 语法:cd [name] n a m e:目录名、路径或目录缩写。 例如: cd 改变目录位置至用户登录时的工作目录。 cd dir1 改变目录位置至d i r 1目录下。

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

复制文件 语法: cp [-r] 源地址目的地址 例如: cp file1 file2 将文件file1 复制成f i l e 2。 cp file1 dir1 将文件file1 复制到目录dir1 下,文件名 仍为f i l e 1。

复制文件 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 复制整个目录。

移动或更改文件、目录名称 语法: 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。

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

删除目录 语法: rmdir 目录名或rm 目录名 例如: rmdir dir1 删除目录d i r 1,但dir1 下必须没有文件 存在,否则无法删除。 rm -r dir1 删除目录d i r 1及其子目录下所有文件。

删除文件 语法: rm 文件名 例如: rm file1 删除文件名为file1 的文件。 rm f* 删除文件名中以f 为字首的所有文件。

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

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

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

查看目录所占磁盘容量 语法: du [-s] 目录 例如: du dir1 显示目录dir1 的总容量及其子目录的容量 (以KB 为单位)。 du -s dir1 显示目录dir1 的总容量。

文件传输 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 下。

文件传输 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。

文件传输 3. 本地工作站与远程工作站之间的文件传输 必须拥有远程工作站的帐号及密码,才可进行传输工作。 语法: ftp 主机名或ftp ip地址 例如: ftp doc 与远程工作站doc 之间进行文件传输。 Name (doc:user-name): <输入帐号> Password (doc:user-password): <输入密码> ftp> help 列出ftp 文件传输时可使用的命令。 ftp> !ls 列出本地工作站当前目录下的所有文件名。 ftp> !pwd 列出本地工作站当前所在的目录位置。 ftp> ls 列出远程工作站当前目录下的所有文件名。

文件传输 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 的所有文件拷 贝到本地工作站中。

文件传输 ftp> mput *.txt 将本地工作站中扩展文件名为txt 的所有文件 拷贝到远程工作站中。 ftp> prompt 切换交互式指令(使用mput/mget 时不是每个文 件皆询问y e s / n o )。 ftp> quit 结束ftp 工作。 ftp> bye 结束ftp 工作。 注意从PC与工作站间的文件传输也可透过在PC端的FTP指 令进行文件传输,指令用法与上述指令大致相同。

文件权限设定 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(其他用户)。

文件权限设定 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,删除其他使用者 可读取的权利。

文件权限设定 2.改变文件或目录的所有权 语法:chown [-R] 用户名 name n a m e:文件名或目录名。 例如: chown user file1 将文件file1 改为用户user 所有。 chown -R user dir1 将目录d i r 1及其子目录下面的 所有文件改为用户user 所有。

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

改变文件或目录工作组所有权 语法:chgrp [-R] 工作组名 name n a m e:文件名或目录名 例如: chgrp vlsi file1 将文件file1 的工作组所有权改为vlsi 工作组所有。 chgrp -R image dir1 将目录d i r 1及其子目录下面的 所有文件,改为image 工作组所有。

改变文件或目录最后修改时间 语法:touch name n a m e:文件名或目录名。

文件的链接 同一文件,可拥有一个以上的名称,也就是把一个 文件进行链接。 语法:ln 老文件名 新文件名 例如: ln file1 file2 将文件f i l e 2链接至文件f i l e 1。

文件中字符串查寻 语法:grep string file 例如: grep abc file1 寻找文件f i l e 1中包含字符串abc 所 在行的文本内容。

寻找文件或命令的路径 语法:whereis command 显示命令的路径。 语法:which command 显示命令的路径,及使用者 所定义的别名。 语法:whatis command 显示命令功能的摘要。 语法:find search-path -name filename -print 搜寻 指定路径下某文件的路径。 例如: find / -name file1 -print 自根目录下寻找文件file1 的 路径。

比较文件或目录的内容 语法:d i ff [-r] name1 name2 name1 name2:可同时为文件名或目录名。 例如: d i ff file1 file2 比较文件file1 与file2 内各行的不同 之处。 d i ff -r dir1 dir2 比较目录dir1 与dir2 内各文件的不 同之处。

文件打印输出 用户可用 set 命令来设定打印机名。 例如: set -a PRINTER=sp 设定自sp 打印机打印资料。 语法:lpr [-P打印机名] 文件名 lpr file1 或lpr -Psp file1 自s p打印机打印文件f i l e 1。

文件打印输出 语法:enscript [-P打印机名] 文件名 例如: enscript file3 或enscript -Psp file3 自s p打印机打印 文件f i l e 3。

打印机控制命令 1.检查打印机状态、打印作业顺序号和用户名 语法:lpq [-P 打印机名] 例如: lpq 或lpq -Psp 检查sp 打印机的状态。

打印机控制命令 2. 删除打印机内的打印作业( 用户仅可删除自己的 打印作业) 语法:lprm [-P打印机名] 用户名或作业编号 例如: lprm user或lprm -Psp user 删除s p打印机中用户 user 的打印作业,此时用户名必须为u s e r。 lprm -Psp 456 删除sp 打印机上编号为456 的打印作 业。

进程控制命令 1.查看系统中的进程 语法:ps [-aux] 例如: p s或ps -x 查看系统中,属于自己的进程。

进程控制命令 2. 结束或终止进程 语法:kill [-9] PID P I D:利用ps 命令所查出的进程号。 例如: kill 456或kill -9 456 终止进程号为456 的进程。

进程控制命令 3. 在后台执行进程的方式 语法:命令& 例如: cc file1.c & 将编译file1.c 文件的工作置于后台行。

进程控制命令 语法:按下C o n t r o l + Z键,暂停正在执行的进 程。键入b g命令,将暂停的进程置于后台继 续执行。 例如: cc file2.c ^ Z S t o p p e d b g

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

进程控制命令 5. 结束或终止后台中的进程 语法:kill %n n:利用j o b s命令查看出的后台作业号 例如:

shell变量 1. 查看外壳变量的设定值 语法:set 查看所有外壳变量的设定值。 语法:echo $变量名显示指定的外壳变量的设定 值。

shell变量 2. 设定外壳变量 语法:set var = value 例如: set term=vt100 设定外壳变量t e r m为VT100 型终 端。

shell变量 3. 删除外壳变量 语法:unset var 例如: unset PRINTER 删除外壳变量PRINTER 的设定 值。

环境变量 1. 查看环境变量的设定值 语法:s e t 查看所有环境变量的设定值。 语法:echo $NAME 显示指定的环境变量N A M E 的设定值。 例如: echo $PRINTER 显示环境变量PRINTER 的设定 值。

环境变量 2. 设定环境变量 语法:set –a NAME=word 例如: set -a PRINTER=sp 设定环境变量PRINTER 为 sp。

环境变量 3. 删除环境变量 语法:unset NAME 例如: unset PRINTER 删除环境变量P R I N T E R的设定 值。

别名 1. 查看所定义的命令的别名 语法: a l i a s 查看自己目前定义的所有命令,及 所对应的别名。 语法: alias name 查看指定的name 命令的别名。 例如: alias dir 查看别名dir 所定义的命令。 ls -atl

别名 2. 定义命令的别名 语法: alias name‘command line’ 例如: alias dir ‘ls -l’ 将命令ls - l 定义别名为d i r。

别名 3. 删除所定义的别名 语法: unalias name 例如: unalias dir 删除别名dir 的定义。

历史命令 1. 设定命令记录表的长度 语法: set history = n 例如: 执行过的前面40 个命令)。

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

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

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

重定向 1. 标准输入的控制 语法:命令< 文件将文件做为命令的输入。 例如: mail -s “mail test” wesongzhou@hotmail.com < file1 将文件file1 当做信件的内容,主题名称为mail test,送给收信人。

重定向 2. 标准输出的控制 语法:命令> 文件将命令的执行结果送至指定的文 件中。 例如: ls -l > list 将执行“ls -l” 命令的结果写入文件list 中。

重定向 语法:命令>! 文件将命令的执行结果送至指定的文 件中,若文件已经存在,则覆盖。 例如: ls -lg >! list 将执行“ls - lg” 命令的结果覆盖写入文 件list 中。

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

重定向 语法:命令>> 文件将命令执行的结果附加到指定的文件 中。 例如: ls - lag >> list 将执行“ls - lag” 命令的结果附加到文件list 语法:命令>>& 文件将命令执行时屏幕上所产生的任何信息 附加到指定的文件中。 cc file2.c >>& error 将编译file2.c 文件时屏幕所产生的任何 信息附加到文件error 中。

管道命令 语法:命令1 | 命令2 将命令1的执行结果送到命令 2,做为命令2的输入。 例如: ls -Rl | more 以分页方式列出当前目录及其子目录下 所有文件的名称。 cat file1 | more 以分页方式列出文件file1 的内容。

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

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

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

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

脚本程序 #!/bin/sh # first.sh # This file looks through all the files in the current # directory for the string POSIX, and then displays those # files to the standard output.

脚本程序 For file in * do if grep –q POSIX $file then more $file fi Done exit 0

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

shell程序设计的语法 变量:字符串、数字、环境和参数 条件:shell中的布尔值 程序控制:if、elif、for、while、until、case等 命令表 函数 内建在shell中的命令 获取某个命令的执行结果 即时文档(here文档)

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

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

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

引号的用法 #!/bin/sh myvar=“Hi there” echo $myvar echo “$myvar”

引号的用法 echo Enter some text read myvar echo ‘$myvar’ now equals $myvar exit 0

引号的用法 程序输出结果如下: Hi there $myvar Enter some text Hello World $myvar mow equals Hello World

环境变量 环境变量是shell预先初始化的一些变量,环境变量可被子进程继承。环境变量通常使用大写字母作名字。具体有哪些环境变量取决于个人配置。 $HOME 当前用户的登陆子目录 $PATH 以冒号分隔的用来搜索命令的子目录清单 $PS1 命令行提示符 $PS2 辅助提示符,用来提示后续输入

环境变量 $IFS 输入区的分隔符。shell读取输入数据时会将一组字符视为单词之间的分隔字符,他们通常是空格、制表符和换行符 $# 传递到脚本程序的参数个数 $$ 该shell脚本程序的进程ID

参数变量 shell脚本程序在调用时还带有参数,会产生参数变量。 $1,$2,… 脚本程序的第一个参数、第二个参数、… $1,$2,… 脚本程序的第一个参数、第二个参数、… $* 全体参数组成的清单,各参数之间用环境变量IFS中的第一个字符分隔开 $@ 全体参数组成的清单,不使用IFS分隔符

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

参数和环境变量的例子 #!/bin/sh salutation=“hello” echo $salutation echo “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”

参数和环境变量的例子 echo “Please enter a new greeting” read salutation echo $salutation echo “The script is now complete” exit 0

参数和环境变量的例子输出 $ ./try_variables foo bar baz Hello The program ./try_variables is now running The second parameter was bar The first parameter was foo The parameter list was foo bar baz The user’s home directory is /home/rick Please enter a new greeting Sire The script is now complete $

条件测试 shell的条件测试命令可以对命令的退出码、字符串比较、算术比较、文件属性进行测试。shell的条件测试命令(布尔判断命令)有test和[]。 例如,检查一个文件是否存在代码如下: if test –f fred.c then … fi

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

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

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

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

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

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

控制结构-if语句 if condition then statements else fi

控制结构-if语句例子 #!/bin/sh echo “Is it morning? Please answer yes or no” read timeofday if [ $timeofday = “yes” ]; then echo “Good morning”

控制结构-if语句例子 else echo “Good afternoon” fi exit 0 如果直接输入回车键会怎样?

控制结构-elif语句 #!/bin/sh echo “Is it morning? Please answer yes or no” read timeofday if [ “$timeofday” = “yes” ]; then echo “Good morning”

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

控制结构-for语句 for variable in values do statements done

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

控制结构-for语句 结果: bar fud 43

控制结构-for语句 例子: #!/bin/sh for file in $(ls f*.sh) do lpr $file done exit 0

控制结构-while语句 while condition do statements done

控制结构-while语句 例子:口令字检查程序 #!/bin/sh echo “enter password” read trythis while [ “$trythis” != “secret” ];do echo “Sorry, try again” done exit 0

控制结构-while语句 例子:命令执行特定的次数 #!/bin/sh foo=1 while [ “$foo” –le 20 ]; do echo “we go again” foo=$(($foo+1)) done exit 0

控制结构-until语句 until condition do statements done

控制结构-until语句 例子:报警程序 #!/bin/sh until who | grep “$1” > /dev/null do sleep 60 done echo –e \\a echo “***** $1 has just logged in *****” exit 0

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

控制结构-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”;; esac exit 0

控制结构-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

控制结构-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” ;;

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

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

AND命令表 例子: #!/bin/sh touch file_one rm –f file_two

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

OR命令表 OR命令表允许我们持续执行一系列命令直到有一条成功为止,其后的命令将不再被执行。 语法:statement1 || statement2 || statement3 || …

OR命令表 例子: #!/bin/sh rm –f file_one if [ -f file_one ] || echo “hello” || echo “there” then echo “in if”

OR命令表 else echo “in else” fi exit 0

语句块 在只允许使用单个语句的地方使用多条语句,可以把它们括在花括号{}里来构造一个语句块。

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

函数 语法: function_name(){ statements }

函数 例子: #!/bin/sh foo(){ echo “Function foo is executing” } echo “script starting” foo echo “script ended” exit 0

函数-局部变量 #!/bin/sh sample_text=“globle varable” foo(){ local sample_text=“local variable” echo “Function foo is executing” echo $sample_text }

函数-局部变量 echo “script starting” echo $sample_text foo echo “script ended” exit 0

命令-break break命令用于从for、while或until循环里中途退出 例子: #!/bin/sh rm –rf fred* echo > fred1 echo > fred2 mkdir fred3 echo > fred4

命令-break for file in fred* do if [ -d “$file” ]; then break; fi done echo first directory starting fred was $file rm –rf fred* exit 0

命令-continue continue命令使for、while或until循环跳到下一个循环继续执行,循环变量取循环清单里的下一个值。 例子: #!/bin/sh rm –rf fred* echo > fred1 echo > fred2 mkdir fred3 echo > fred4

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

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

命令-: 例子: #!/bin/sh rm –f fred if [ -f fred ]; then : else echo file fred did not exist fi exit 0

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

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

命令- . 执行结果 $ . ./classic_set classic> echo $version classic classic>. ./latest_set latest> echo $version latest latest>

命令- eval eval命令对参数进行求值操作。例子: foo=10 x=foo y=‘$’$x echo $y 输出是$foo

命令- eval foo=10 x=foo eval y=‘$’$x echo $y 输出是10

命令- exec exec命令被用来以另一个不同程序替换掉当前的shell。例子: exec wall “Thank you for all the fish” 这个命令会用wall替换掉当前的shell,脚本中后面的程序就不会执行了。

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

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

命令- export 脚本export1: #!/bin/sh foo=“This is foo” export bar=“This is bar” export2 脚本export1运行结果:This is bar

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

命令- expr expr1 | expr2 如果expr1非零则等于expr1,否则等 于expr2

命令- expr expr1 != expr2 不等于 expr1 + expr2 加法 expr1 - expr2 减法

命令- set set命令的作用是为shell设定参数变量。 例子:在shell脚本里使用当前月份的名字 #!/bin/sh echo the date is $(date) set $(date) echo the month is $2 exit 0

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

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

命令的执行 我们希望执行一条命令并将命令的输出放到一个变量里。注意:是命令的输出而不是返回值。 语法:$(command)

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

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

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

命令的执行-参数扩展 语法:${variable} 例子:处理1_tmp和2_tmp文件(不能正常执行的情况) #!/bin/sh for i in 1 2 do grep POSIX $i_tmp done exit 0

命令的执行-参数扩展 语法:${variable} 例子:处理1_tmp和2_tmp文件(正常执行的情况) #!/bin/sh for i in 1 2 do grep POSIX ${i}_tmp done exit 0

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

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

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

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

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

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

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];     /* 可以忽略 */ };

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 */

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;       /* 可以忽略 */ };

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。

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 */ };

与文件操作有关的系统调用 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 nbytes); size_t write(int fildes, const void *buf, size_t nbytes); int close(int fildes); int ioctl(int fildes, int cmd,…);

与文件操作有关的系统调用 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);

与文件操作有关的系统调用 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);

与文件操作有关的系统调用 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);

系统调用-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);

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

系统调用-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 其他用户读权限

系统调用-open unmask 系统变量为3个八进制数,分别对应所有者、同组用 户、其他用户,如对应位置位则表示禁止相应的权限。 0-不禁止任何权限、1-禁止执行权限、2-禁止写权限、4-禁 止读权限 返回值 如果成功返回文件描述符,失败返回-1,全局变量 errno保存出错码

系统调用-read #include <unistd.h> size_t read(int fildes, const void *buf, size_t nbytes); fildes 为文件描述符 buf 为缓冲区指针 nbytes 为要读的字节数 返回值 为实际读的字节数、-1为错误

系统调用-read #include <unistd.h> #include <stdlib.h> int main() { char buffer[128]; int nred; nred=read(0,buffer,128);

系统调用-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); }

系统调用-write #include <unistd.h> size_t write(int fildes, const void *buf, size_t nbytes); fildes 为文件描述符 buf 为缓冲区指针 nbytes 为要写的字节数 返回值 为实际写的字节数、-1为错误

系统调用-close #include <unistd.h> int close(int fildes); close中止文件描述符fildes与文件之间的关联 返回值 0-成功、-1-错误

系统调用-lseek #include <unistd.h> #include <sys/types.h> off_t lseek(int fildes, off_t offset, int whence); Lseek对文件描述符fildes的读写指针进行设置,用来设置 文件的下一个读写位置。即可以把指针设置到绝对位置,也 可以设置到相对于当前位置或文件尾的某个相对位置。

系统调用-lseek offset 偏移量 whence 可以取下列值: SEEK_SET offset是一个绝对值 SEEK_CUR offset是从当前位置算起的一个相对位置 SEEK_END offset是从文件尾算起的一个相对位置 返回值 文件头到新指针位置的偏移量、-1-失败

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)); }

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)); }

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);

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); }

系统调用-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)

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

系统调用-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; /* 最后一次改变时间(指属性) */ };

系统调用-dup,dup2 #include <unistd.h> int dup(int fildes); int dup2(int fildes, int fildes2) dup系统调用将一个文件描述符拷贝到该用户文件描述符表 中的第一个空槽中返回一个新的文件描述符。dup2系统调用 将文件描述符fildes复制给fildes2。

系统调用-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();

系统调用-dup,dup2 close(1); dup(fd); for(i=0; i<sizeof(buf); i++) buf[i]=‘a’; write(1, buf, sizeof(buf)); }

系统调用-pipe #include<unistd.h> int pipe(int fildes[2]); 们可以访问文件描述符fildes[0],fildes[1].其中fildes[0] 是用来读的文件描述符,而fildes[1]是用来写的文件描述符.

系统调用-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

系统调用-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); }

系统调用-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); }

系统调用-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); } }

系统调用-chmod #include <sys/stat.h> int chmod(const char *path, mode_t mode); chmod系统调用修改路径名path文件的访问权限,mode设置 类似于open中的mode。

系统调用-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的符号链接。

系统调用-mkdir,rmdir #include <sys/stat.h> int mkdir(const char *path, mode_t mode); int rmdir(const char *path); mkdir和rmdir用来创建和删除目录,

系统调用-chdir #include <unistd.h> int chdir(const char *path);

系统调用-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时 仍保持开放。

系统调用-fcntl cmd=F_GETFD 获取文件描述符标志, 唯一标志: FD_CLOEXEC。对应于filedes的文件描述符标 志作为函数值返回。 cmd=F_SETFD 对于filedes设置文件描述符标志。新标志值 是按第3个参数(取为整型值) 设置的 0-在 exec时不关闭 1-在exec时关闭。 cmd=F_GETFL 获取文件状态标志。对应于filedes的文件状 态标志作为函数值返回。在我们说明open函 数时, 已说明了文件状态标志

系统调用-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。

系统调用-mount,umount mount系统调用用来安装一个设备。 语法:mount(const char *dev, const char *dir, int arg); umount卸载一个设备 语法:umount(const char *dev);

系统调用-综合应用 读目录的函数 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 */ }

系统调用-综合应用 例子:一个类似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

系统调用-综合应用 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; }

系统调用-综合应用 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; }

系统调用-综合应用 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);     } }

系统调用-综合应用 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);

系统调用-综合应用 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);     }

系统调用-综合应用     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);         }

系统调用-综合应用         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);                }

系统调用-综合应用                 else                {                     while (rn = read (pthid, rwbuf, RWBUFSIZ))                         write (opthid, rwbuf, rn);                     count++;                     close(opthid);                }                 close(pthid);            }        }         return count;    } }

有关进程的一些概念 进程:地址空间+系统资源。地址空间和系统资源被属于同 一进程的一个或者多个线程所使用。一个进程包括程 序代码、数据、堆栈、打开的文件和环境。 线程:是位于一个进程的地址空间内、可被单独调度运行的 单元。又称为轻量进程。 进程的父子关系、僵死子进程、进程的真正用户标识 号、有效用户标识号、保存的用户标识号、setuid程序、信 号(软中断)。

当前目录 核心栈 父进程数据 打开的文件 改变的根目录 父进程 U区 本进程区表 父进程用户栈 子进程数据 子进程 子进程用户栈 共享正文 文件表 索引节点表

与进程相关的系统调用 pid_t fork(void); int execl(const char *path, const char *arg0, …, (char *)0); int execlp(const char *file, const char *arg0, …, 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[]);

与进程相关的系统调用 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);

与进程相关的系统调用 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);

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

系统调用-fork #include <unistd.h> int pid_t fork(void); 间,并继承父进程的用户代码、组代码、环境变量、已打开的文 件、工作目录和资源限制等。子进程不会继承父进程的文件锁和 未处理的信号。 返回值:如果fork成功则在父进程中返回子进程的pid,在子进程中返 回0。如果失败则返回-1,错误码保存于errno中。 错误码:EAGAIN 内存不足 ENOMEM 内存不足,无法配置核心所需的数据空间。

系统调用-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(); }

系统调用-fork rdwrt() { for(;;) if(read(fdrd, &c, 1)!=1)return; write(fdwt, &c, 1); }

系统调用-execve #include <unistd.h> int execve(const char *path, const char *argv[], const char *envp[]); 说明:execve()用path指定的文件替换当前进程的 内存映像,argv为传给main的参数,envp为新 的环境变量。 返回值:成功则不会返回。如果失败则返回-1,错 误码保存于errno中。

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

系统调用-execve #include <unistd.h> main() { char *argv[]={“ls”,”-al”,”/etc/passwd”,(char *)0}; char *envp[]={“PATH=/bin”,0}; execve(”/bin/ls”,argv,envp); }

系统调用-wait #include <sys/types.h> #include <sys/wait.h> int pid_t wait(int *status); 说明:wait()暂停目前进程的运行,直到有信号来或子进程 结束。如果在调用wait时子进程已经结束则wait会立 即返回子进程的结束状态值。如果进程捕俘“子进程 死”信号,则调用用户的软中断信号处理程序。如果 进程忽略“子进程死”软中断信号,则wait释放僵死子 进程的进程表项,然后寻找其他的子进程。 返回值:成功返回子进程PID。

系统调用-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); }

系统调用-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); }

系统调用-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);

系统调用-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);

系统调用-waitpid #include <sys/types.h> #include <sys/wait.h> pid_t waitpid(pid_t pid, int *stat_loc, int options); 说明:waitpid()暂停目前进程的运行,直到有信号来或子 进程结束。如果在调用wait时子进程已经结束则wait 会立即返回子进程的结束状态值。

系统调用-waitpid 参数 pid为欲等待的子进程识别码 pid<-1等待gid为pid绝对值的任何子进程 pid=-1等待任何子进程,相当于wait pid=0 等待gid与目前进程gid相同的子进程 pid>0 等待任何识别码为pid的子进程 option 可以为0或下面的OR组合 WNOHANG 如果没有任何已经结束的子进程则马上返回,不等待 WUNTRACED 如果子进程进入暂停执行情况则马上返回,但结束状 态不予理会 返回值:成功返回子进程PID。如果失败则返回-1,错误码 保存于 errno中。

系统调用-waitpid 子进程的结束状态返回后存于status,下面的宏可判别结束情况: WIFEXITED(status) 如果子进程正常结束则为非0值 WEXITSTATUS(status) 取得子进程由exit返回的结束代码,一般会先用 WIFEXITED来判断是否正常结束才能使用此宏 WIFSIGNALED(status) 如果子进程是因为信号而结束则此宏值为真 WTERMSIG(status) 取得子进程因信号而终止的信号代码,一般会先 用WIFSIGNALED来判断才能使用此宏 WIFSTOPPED(status) 如果子进程处于暂停执行的情况则此宏为真 WSTOPSIG(status) 取得引发子进程暂停的信号代码,一般会先用 WIFSTOOPED来判断后才使用此宏

系统调用-signal #include <signal.h> void (*signal(int sig, void (*func)(int)))(int); 说明:signal为指定的信号设置相应的信号处理函数,当指定的信号到 达时执行相应的信号处理函数如果func不是函数指针,则必须是 下列两个常数之一: SIG_IGN 忽略相应的信号 SIG_DFL 将信号处理重置为缺省的处理方式 返回值:成功返回先前的信号处理函数指针。如果失败则返回-1,错误 码 保存于errno中。 注意事项:在接收到一次信号并处理后,系统自动将信号处理方式切换 回缺省的处理方式

系统调用-signal

系统调用-signal

系统调用-signal

系统调用-signal

系统调用-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绝对值的所有进程

系统调用-kill 返回值:如果成功则返回0。如果失败则返回-1,错误码 保存于errno中。 错误码:ENIVAL 参数sig不合法 ESRCH 参数pid所指的进程或进程组不存在 EPERM 权限不够无法将信号发给指定的进程

系统调用-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;

系统调用-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));

系统调用-pause #include <unistd.h> int pause(void); 返回值:只返回-1,错误码保存于errno中。 错误码:EINTR 有信号到达中断了此函数

系统调用-sleep #include <unistd.h> unsigned int sleep(unsigned int seconds); 说明:sleep暂停进程的执行seconds时间,或被信 号唤醒 返回值:若进程暂停到所指定时间则返回0,若有信 号中断则返回剩余秒数。

系统调用-alarm #include <unistd.h> unsigned int alarm(unsigned int seconds); 说明:alarm在过了seconds时间后给进程发信号 SIGALRM。如果seconds为0,则之前设置的 闹钟被取消并返回剩余时间。 返回值:返回之前闹钟剩余的秒数,若之前未设闹 钟返回0。

系统调用-alarm #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <signal.h> void handler(){ printf(“hello\n”); }

系统调用-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); }

系统调用-setuid #include <unistd.h> int setuid(uid_t uid); 只有在有效用户标识号为0(root)的情况下才起作 用。当调用setuid后root权限会丧失,将不再能调用 setuid。如只是暂时放弃root权限需用seteuid。 返回值:执行成功返回0。如果失败则返回-1,错误码保存 于errno中。

系统调用-seteuid #include <unistd.h> int seteuid(uid_t uid); 进程的用户标识号为root则将有效用户标识号设置为 uid,如果进程的用户标识号不是超级用户,而且uid 的值是真正用户标识号或保存的用户标识号则将有效 用户标识号改为uid。否则返回一个错误。 返回值:执行成功返回0。如果失败则返回-1,错误码保存 于errno中。

系统调用-seteuid 例子: 假定以下程序的所有者为maury(用户标识号为8319), setuid位被置位,且所有用户都有权执行该文件。 假定用户mjb(用户标识号为5088)和用户maury分别拥 有文件mjb和maury,且者两个文件只对他们的所有着具有只 读许可权。

系统系统调用-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);

系统系统调用-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()); seteuid(euid); }

系统调用-getuid #include <unistd.h> #include <sys/types.h> uid_t getuid(void); 说明:getuid用来取得进程的真正用户标识号。 返回值:真正用户标识号。

系统调用-geteuid #include <unistd.h> #include <sys/types.h> uid_t geteuid(void); 说明:geteuid用来取得进程的有效用户标识号。 返回值:有效用户标识号。

系统调用-setgid #include <unistd.h> #include <sys/types.h> int setgid(void); 说明:setgid用来设置进程的真正组标识号。如果 以超级用户身份调用则有效组标识号、真正 组标识号保存的组标识号都设成gid. 返回值:设置成功返回0,失败返回-1。

系统调用-setegid #include <unistd.h> #include <sys/types.h> int setegid(void); 说明:setegid用来设置进程的有效组标识号。 返回值:设置成功返回0,失败返回-1。

系统调用-getgid #include <unistd.h> #include <sys/types.h> gid_t getgid(void); 说明:getuid用来取得进程的真正组标识号。 返回值:真正组标识号。

系统调用-getegid #include <unistd.h> #include <sys/types.h> gid_t getegid(void); 说明:getegid用来取得进程的有效组标识号。 返回值:有效组标识号。

系统调用-setpgid #include <unistd.h> #include <sys/types.h> int setpgid(pid_t pid,pid_t pgid); 说明:setpgid()将参数pid 指定进程的进程组组 号设为参数pgid 指定的组号。如果参数pid 为0,则设置当前进程的组号,如果参数pgid 为0,则会以当前进程的进程标识号来取代原 有的组号。

系统调用-setpgid 返回值:执行成功则返回新的组号,如果有错误则 返回-1,错误原因存于errno中。 错误码:EINVAL 参数pgid小于0。 EPERM 进程权限不足,无法完成调用。 ESRCH 找不到符合参数pid指定的进程。

系统调用-setpgrp #include <unistd.h> #include <sys/types.h> int setpgrp(void); 说明:setpgrp()将当前进程的组号设为当前进程的 进程号。此函数相当于调用相当于 setpgid(0,0)。

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

系统调用-getpgid #include <unistd.h> #include <sys/types.h> pid_t getpgid( pid_t pid); 说明:getpgid()用来取得参数pid 指定进程的组 号。如果参数pid为0,则会取得当前进程的 组号。 返回值:执行成功则返回组号,如果有错误则返回 -1,错误原因存于errno中。

系统调用-getpgrp #include <unistd.h> #include <sys/types.h> pid_t getpgrp(void); 说明:getpgrp()用来取得目前进程所属的组识别 码。此函数相当于调用getpgid(0); 返回值:执行成功则返回组号,如果有错误则返回 -1,错误原因存于errno中。

系统调用-getpid #include <unistd.h> #include <sys/types.h> pid_t getpid(void); 说明:getpid()用来取得当前进程的进程标识号。 返回值:当前进程的标识号。

系统调用-getppid #include <unistd.h> #include <sys/types.h> pid_t getppid(void); 说明:getppid()用来取得当前进程的父进程标识 号。 返回值:当前进程的父进程标识号。

系统调用-nice #include <unistd.h> #include <sys/types.h> int nice(int inc); 说明:nice()用来改变进程的进程的优先级。参数 inc数值越大则优先级越低。只有超级用户才 能使用负的inc 值,提高进程的优先级。

系统调用-exit #include <unistd.h> #include <sys/types.h> void exit(staus); 说明:exit用来正常终结目前进程的执行,并将参 数返回给父进程。 返回值:无。

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

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

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

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

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

信号 信号用于通知异步事件发生,是一种软中断机制。除了用于通知进程系统事件外,还可以用于进程间通信,除此之外,进程还可以发送信号给进程本身。 由于历史上的原因,linux信号分为不可靠信号和可靠信号两类。其中,不可靠信号对应于system V信号,可靠信号对应于BSD和POSIX信号。

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

信号-不可靠信号 不可靠信号是指信号值小于SIGRTMIN (一般情况下,SIGRTMIN=31,SIGRTMAX=63)的信号。不可靠信号主要表现在: 进程每次处理信号后,就将对信号的响应设置为默认动作,如不希望这样用户信号处理函数结尾再一次调用signal()。 信号可能丢失。 注:在linux中对不可靠信号机制做了改进:在调用完信号 处理函数后,不必重新安装该信号的处理函数。因此, linux下的不可靠信号问题主要指的是信号可能丢失。

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

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

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));

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

信号量 主要作为进程间以及同一进程不同线程之间对临界区的互斥访问。

共享内存 使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使用,来达到进程间的同步及互斥。

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

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);

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));

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);

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);

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);

系统调用-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。

系统调用-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。

系统调用-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]; };

系统调用-msgrcv 参数msgsz为消息数据的长度,即mtest参数的长度。 参数msgtyp用来指定所要读取的消息种类: =0 返回队列内第一项消息 >0 返回队列内第一项与msgtyp相同的消息 <0 返回队列内第一项mtype小于或等于msgtyp绝对值的消息 参数msgflg可被设置成以下值: IPC_NOWAIT 如果没有满足条件的消息,调用立即返回,此时, errno=ENOMSG IPC_EXCEPT 与msgtyp>0配合使用,返回队列中第一个类型不为 msgtyp的消息 IPC_NOERROR 如果队列中满足条件的消息内容大于所请求的 msgsz字节,则把该消息截断,截断部分将丢失 返回值:成功返回实际读到的信息长度,否则返回-1。

系统调用-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]; };

系统调用-msgsnd 参数msgsz为消息数据的长度,即mtest参数的长度。 参数msgflg可被设置成IPC_NOWAIT,指示消息队列已满或有其他情况 无法马上送入信息时,立即返回EAGAIN。 返回值:成功返回0,否则返回-1。

系统调用-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结构中;

系统调用-msgctl IPC_SET:该命令用来设置消息队列的属性,要设置的属性存储 在buf指向的msqid结构中,包括:msg_perm.uid、 msg_perm.gid、msg_perm.mode以及msg_qbytes,也影 响msg_ctime成员。 IPC_RMID:删除msqid标识的消息队列;

系统调用-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 */

系统调用-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 */ };

系统调用-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;//序号 };

消息队列系统调用示例 #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;

消息队列系统调用示例 struct msgsbuf{ int mtype; char mtext[1]; }msg_sbuf; struct msgmbuf{ char mtext[10]; }msg_rbuf; struct msqid_ds msg_ginfo,msg_sinfo;

消息队列系统调用示例 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;

消息队列系统调用示例 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);

消息队列系统调用示例 //从消息队列中读出消息后,输出消息队列属性 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);

消息队列系统调用示例 if(reval==-1) { printf("msg set info error\n"); return; } msg_stat(msgid,msg_ginfo); //验证设置消息队列属性 reval=msgctl(msgid,IPC_RMID,NULL);//删除消息队列 printf("unlink msg queue error\n");

消息队列系统调用示例 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);

消息队列系统调用示例 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)));

消息队列系统调用示例 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); }

系统调用-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。

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

系统调用-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 */ };

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

系统调用-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个共享资源。

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

系统调用-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;

系统调用-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.val

系统调用-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; };

系统调用-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 */ };

系统调用-semctl struct 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 */ };

系统调用-semctl struct seminfo { int semmap; int semmni; int semmns; int semmnu; int semmsl; int semopm; int semume; int semusz; int semvmx; int semaem; };

信号量集系统调用示例 #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

信号量集系统调用示例 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");

信号量集系统调用示例 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; }

信号量集系统调用示例 else { if(arg.buf->sem_otime!=0){ i=max_tries; init_ok=1; } else sleep(1); if(!init_ok) {

信号量集系统调用示例 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

信号量集系统调用示例 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");

信号量集系统调用示例 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);

信号量集系统调用示例 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);

信号量集系统调用示例 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");

信号量集系统调用示例 //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"); }

系统调用-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为key 的共享内存。shmflg也用来决定共享内存的存取权限相当于open 的参数mode。 返回值:成功返回共享内存描述符,否则返回-1。

系统调用-shmget 错误码:EINVAL 参数size小于SHMMIN或大于SHMMAX EACCES key指定的共享内存存在但无存取权限 EEXIST key所指的共享内存已存在 EIDRM key所指的共享内存已删除 ENOENT key所指的共享内存不存在 ENOMEM 核心内存不足 ENOSPC 已超过系统允许的共享内存的最大值SHMALL

系统调用-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。

系统调用-shmdt #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> int shmdt(const void shmaddr); 说明:shmdt()用来将连接到shmaddr的共享内存与进程的虚地址空间 断接。 返回值:成功0,否则返回-1。

系统调用-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;

系统调用-shmctl IPC_RMID:删除shmid标识的共享内存; SHM_LOCK:禁止共享内存兑换到swap,超级用户可用; SHM_UNLOCK:允许共享内存兑换到swap,超级用户可用;

系统调用-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; };

共享内存系统调用示例 #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;

共享内存系统调用示例 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);

共享内存系统调用示例 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); }

系统调用-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代表将文件中多大的部分对应到内 存。

系统调用-mmap 参数:prot代表映射区域的保护方式有下列组合 PROT_EXEC 映射区域可被执行 PROT_READ 映射区域可被读取 PROT_WRITE 映射区域可被写入 PROT_NONE 映射区域不能存取

系统调用-mmap 参数:flags会影响映射区域的各种特性 MAP_FIXED 如果参数start所指的地址无法成功建立映射时,则 放弃映射,不对地址做修正。 MAP_SHARED 对映射区域的写入数据会复制回文件内,而且允许 其他映射该文件的进程共享。 MAP_PRIVATE 对映射区域的写入操作会产生一个映射文件的复 制,即私人的“写入时复制”(copy on write)对 此区域作的任何修改都不会写回原来的文件内容 MAP_ANONYMOUS 建立匿名映射。此时会忽略参数fd,不涉及文 件,而且映射区域无法和其他进程共享。

系统调用-mmap 参数: MAP_DENYWRITE 只允许对映射区域的写入操作,其他对文件直接 写入的操作将会被拒绝。 MAP_LOCKED 将映射区域锁定住,这表示该区域不会被对换。 在调用mmap()时必须要指定MAP_SHARED 或MAP_PRIVATE。 参数:fd为open()返回的文件描述词,代表欲映射到内存的文件。 参数:offset为文件映射的偏移量,通常设置为0,代表从文件最前方 开始对应,offset必须是分页大小的整数倍。 返回值:若映射成功则返回映射区的内存起始地址,否则返回 MAP_FAILED(-1),错误原因存于errno 中。

系统调用-mmap 错误码:EBADF 参数fd 不是有效的文件描述词 EACCES 存取权限有误。如果是MAP_PRIVATE 情况下文件必须可 读,使用MAP_SHARED则要有PROT_WRITE以及该文件要 能写入。 EINVAL 参数start、length 或offset有一个不合法。 EAGAIN 文件被锁住,或是有太多内存被锁住。 ENOMEM 内存不足。 EINVAL 参数size小于SHMMIN或大于SHMMAX

系统调用-munmap #include <unistd.h> #include <sys/mman.h> int munmap(void *start, size_t length); 说明:munmap()用来取消参数start所指的映射内存起始地址,参数 length则是欲取消的内存大小。当进程结束或利用exec相关函数 来执行其他程序时,映射内存会自动解除,但关闭对应的文件描 述符时不会解除映射。 返回值:如果解除映射成功则返回0,否则返回-1。 错误码:EINVAL 参数 start或length 不合法。

内存映射系统调用示例 #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*/

内存映射系统调用示例 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); }

设备驱动程序 设备驱动程序是操作系统内核代码的一部分,位于系统调用与物理设备之间,为操作系统访问设备提供一致的、抽象的接口,屏蔽具体物理设备的操作细节,根据操作系统的要求完成系统与物理设备之间的数据传输。

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

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

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

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

内核功能模块的划分 设备控制 几乎每种系统操作最后都要映射到物理设备上。除了处理器,内存和少数其他实体外,几乎所有设备的控制操作都由设备相关的代码来实现。这些代码就是设备驱动程序。内核必须为每个外部设备嵌入设备驱动程序,从硬盘驱动器到键盘和磁带。内核的这方面功能就是本书的着眼点。

内核功能模块的划分 网络 网络必须由操作系统来管理,由于大多数网络操作不是针对于进程的:接收数据包是异步事件。数据包必须在进程处理它们以前就被收集,确认和分发。系统通过程序和网络接口发送数据包,并且应该可以正确地让程序睡眠,并唤醒等待网络数据的进程。另外,所有路由和地址解析问题是在内核里实现的。

设备驱动程序与其他部分的关系

设备驱动程序的分类 字符设备 可以象文件一样访问字符设备,字符设备驱动程序通常会实现open,close,read和write系统调用。系统控制台和并口就是字符设备的例子,它们可以很好地用流概念描述。通过文件系统节点可以访问字符设备,例如/dev/tty1和/dev/lp1。在字符设备和普通文件系统间的唯一区别是:普通文件允许在其上来回读写,而大多数字符设备仅仅是数据通道,只能顺序读写。当然,也存在这样的字符设备,看起来象个数据区,可以来回读取其中的数据。

设备驱动程序的分类 块设备 块设备是文件系统的宿主,如磁盘。以块为单位进行访问,一个块通常是1K字节数据。块设备和字符设备只在内核内部的管理上有所区别,也就是在内核/驱动程序间的软件接口上有所区别。每个块设备也通过文件系统节点来读写数据,它们之间的不同对用户来说是透明的。块设备驱动程序和内核的接口和字符设备驱动程序的接口是一样的,它也通过一个传统的面向块的接口与内核通信,但这个接口对用户来说时不可见的。

设备驱动程序的分类 网络设备 任何网络事务处理都是通过网络接口实现的,通常,接口是一个硬件设备,但也可以象loopback(回路)接口一样是软件工具。网络接口是由内核网络子系统驱动的,它负责发送和接收数据包,而且无需了解每次事务是如何映射到实际被发送的数据包。尽管“telnet”和“ftp”连接都是面向流的,它们使用同样的设备进行传输;但设备并没有看到任何流,仅看到数据报。由于不是面向流的设备,所以网络接口不能象/dev/tty1那样简单地映射到文件系统的节点上。Unix调用这些接口的方式是给它们分配一个独立的名字(如eth0)。这样的名字在文件系统中并没有对应项。内核和网络设备驱动程序之间的通信与字符设备驱动程序和块设备驱动程序与内核间的通信是完全不一样的。内核不再调用read,write,它调用与数据包传送相关的函数。

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

设备驱动程序的注意事项 只能使用核心提供的功能函数 代码应为可重入的 提供机制(mechanism),不实现策略(policy) 不影响系统安全

设备驱动程序的相关概念 主设备号与次设备号 系统使用主设备号和次设备号标识一个设备,一般主设备号用于标识设备驱动程序,次设备号用于标识同一个设备驱动程序管理的不同设备。

设备驱动程序的相关概念 核心用定义于<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);

设备驱动程序的相关概念 设备文件 块设备和字符设备被组织到文件系统中,通过文件系统调用进行访问。每一个设备由一个设备文件表示,设备文件存储设备的主设备号和次设备号,访问权限等等。

设备驱动程序的相关概念

设备驱动程序的相关概念 模块(module) 模块是可以运行时被加载到内核的功能模块,加载后模块作为内核功能的一部分运行于核心态,模块在不使用的时候,可以被动态地从核心卸载。模块一般实现为共享库。

设备驱动程序的相关概念 模块的装载 模块的装载使用命令insmod或modprobe,insmod装载一个模块,modprobe装载有依赖关系的所有模块。insmod和modprobe使用系统调用sys_init_module完成模块的装载。 系统调用sys_init_module首先为模块分配系统内存,然后将模块正文拷贝进核心内存,接下来进行符号解析,最后调用模块的初始化函数。

设备驱动程序的相关概念 #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; }

设备驱动程序的相关概念 static void hello_exit(void) { printk(KERN_ALERT "Goodbye, cruel world\n"); } module_init(hello_init); module_exit(hello_exit);

设备驱动程序的相关概念 加载模块命令:insmod ./hello.ko 卸载模块命令:rmmod hello

设备驱动程序的相关概念 内核符号表 为支持模块的动态加载与卸载核心维护一个内核符号表,内核符号表记录所有核心导出的符号(函数和全局变量)及其地址。

设备驱动程序的相关概念 内核符号表 为支持模块的动态加载与卸载核心维护一个内核符号表,内核符号表记录所有核心导出的符号(函数和全局变量)及其地址。被装载的模块也可以将自身的全局变量输出到核心符号表中,但要注意符号污染问题。 EXPORT_SYMBOL(name); EXPORT_SYMBOL_GPL(name);

设备驱动程序的相关概念 用户空间与核心空间 核心需要在核心地址空间和用户空间交换数据,核心代码使用用户空间的地址要做仔细的检查。

设备驱动程序的相关概念 访问用户地址空间: 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);

设备驱动程序的相关概念 分配核心内存: void *kmalloc(size_t size, int flags); void kfree(void *ptr);

设备驱动程序的相关概念 当前进程 驱动程序有时需要访问当前进程的信息。当前进程是正在执行系统调用的进程。

设备驱动程序的相关概念 对当前进程的访问可以通过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);

设备驱动程序的相关概念 核心栈 模块与操作系统核心共享核心栈,核心栈较小,一般为一个页面大小,因此不要在模块中使用大的临时变量。

设备驱动程序的相关概念 初始化函数 初始化函数在模块被加载的时候调用,调用完以后所占用的空间被系统释放,因此不要在模块中调用初始化函数。

设备驱动程序的相关概念 static int __init initialization_function(void) { /* Initialization code here */ } module_init(initialization_function);

设备驱动程序的相关概念 清除函数 清除函数在模块被删除的时候调用,主要用于释放模块申请的系统资源。

设备驱动程序的相关概念 static void __exit cleanup_function(void) { /* Cleanup code here */ } module_exit(cleanup_function);

设备驱动程序的相关概念 模块参数 模块经常需要一些入口参数指明模块应如何动作。linux支持模块定义在被加载时要接收的参数。 注:模块和模块参数信息将被记录在文件 /proc/modules和文件/sys/module中

设备驱动程序的相关概念 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"

设备驱动程序的相关概念 支持的模块参数类型: bool int long short uint ulong ushort charp

设备驱动程序的相关概念 模块装载的竞争条件 模块一旦被注册则立即可被系统访问,有可能出现初始化过程尚未结束时候,系统引用已经被注册的部分。 初始化失败的时候,系统可能已经引用了被注册的功能。

主设备号的分配和释放 建立设备驱动程序首先需要为设备分配一个主设备号。linux提供用户指定和动态分配两种方式来分配主设备号。

主设备号的分配和释放 静态分配使用如下函数: #include <linux/fs.h> int register_chrdev_region(dev_t first, unsigned int count, char *name); first 为申请分配的连续设备号的起始设备号 count 为申请分配的设备号数量 name 为与设备号相关联的设备名 注:设备名将被记录在文件/proc/devices文件中

主设备号的分配和释放 动态分配使用如下函数: #include <linux/fs.h> int alloc_chrdev_region(dev_t *dev, unsigned int firstminor, unsigned int count, char *name); dev 保存被分配的第一个主设备号 firstminor 为申请的次设备号,通常为0

主设备号的分配和释放 如果不在使用一个设备号的时候,需要将设备号释放。 设备号的释放使用如下函数: #include <linux/fs.h> void unregister_chrdev_region(dev_t first, unsigned int count);

用动态分配的主设备号创建设备 动态分配的主设设备号无法用来预先创建设备文件,因此为动态分配主设备号的设备创建文件需要用文件/proc/ devices。/proc/devices文件内容如下: Character devices: 1 mem 2 pty 3 ttyp Block devices: 2 fd 8 sd 11 sr

用动态分配的主设备号创建设备 为动态分配设备号的设备创建文件,需要使用脚本文件在insmod模块后,使用/proc/devices的设备号创建文件。 #!/bin/sh module="scull" device="scull" mode="664" /sbin/insmod ./$module.ko $* || exit 1 rm -f /dev/${device}[0-3]

用动态分配的主设备号创建设备 major=$(awk "\\$2= =\"$module\" {print \\$1}" /proc/devices) mknod /dev/${device}0 c $major 0 mknod /dev/${device}1 c $major 1 mknod /dev/${device}2 c $major 2 mknod /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]

灵活地创建设备 创建设备的较为理想的方法是缺省使用动态分配的设备 号,保留指定设备号的能力。

灵活地创建设备 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_major = MAJOR(dev); } if (result < 0) { printk(KERN_WARNING "scull: can't get major %d\n", scull_major); return result;

设备驱动程序相关的核心数据结构 基本的设备驱动程序需要引用三个核心数据结构 file_operations 用来建立设备驱动程序的操作与设备文件的对应关系。是由一些列函数指针组成的结构。被打开的设备文件的文件表中有一个域(f_op)指向file_operations结构。该结构中的函数主要用于实现对设备文件的open、read、write等系统调用。file_operations结构和指向该结构的指针一般被称为fops。

设备驱动程序相关的核心数据结构 file 每一个file结构表示一个被打开的文件,它由open系统调用创建,并被传送给每一个对文件操作的函数。一般用file和filp分别指file结构和指向file结构的指针。 inode 内核使用inode结构表示一个文件。

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); };

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*/ };

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;

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;

inode结构 从索引节点获得设备号: unsigned int iminor(struct inode *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);

字符设备驱动程序-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; };

字符设备驱动程序-scull scull设备的存储结构如下:

字符设备驱动程序-scull scull设备的存储结构如下: struct scull_qset { void **data; struct scull_qset *next; };

字符设备驱动程序-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);

字符设备驱动程序-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 */ };

字符设备驱动程序-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); }

字符设备驱动程序-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 */

字符设备驱动程序-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 */

字符设备驱动程序-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; }

字符设备驱动程序-scull next = dptr->next; kfree(dptr); } dev->size = 0; dev->quantum = scull_quantum; dev->qset = scull_qset; dev->data = NULL; return 0;

字符设备驱动程序-scull int scull_release(struct inode *inode, struct file *filp) { return 0; }