每个人都是一座孤岛,却依然可以拥有海洋的怀抱!
系统DLL文件修改
一、DLL文件常识
DLL文件即动态链接库文件,是一种可执行文件,它允许程序共享执行特殊任务所必需的代码和其他资源。Windows提供的DLL文件中包含了允许基于Windows的程序在Windows环境下操作的许多函数和资源。
DLL多数情况下是带有DLL扩展名的文件,但也可能是EXE或其他扩展名。它们向运行于Windows操作系统下的程序提供代码、数据或函数。程序可根据DLL文件中的指令打开、启用、查询、禁用和关闭驱动程序。
DLL的全称是Dynamic Link Library, 中文叫做“动态链接文件”。在Windows操作系统中, DLL对于程序执行是非常重要的, 因为程序在执行的时候, 必须链接到DLL文件, 才能够正确地运行。而有些DLL文件可以被许多程序共用。因此, 程序设计人员可以利用DLL文件, 使程序不至于太过巨大。但是当安装的程序越来越多, DLL文件也就会越来越多, 如果当你删除程序的时候, 没有用的DLL文件没有被删除的话, 久而久之就造成系统的负担了。
DLL是动态连接库。使用动态连接库的一些好处是:
1.多个应用程序共享代码和数据:比如Office软件的各个组成部分有相似的外观和功能,这就是通过共享动态连接库实现的。
2.在钩子程序过滤系统消息时必须使用动态连接库。
3.动态连接库以一种自然的方式将一个大的应用程序划分为几个小的模块,有利于小组内部成员的分工与合作。而且,各个模块可以独立升级。如果小组中的一个成员开发了一组实用例程,他就可以把这些例程放在一个动态连接库中,让小组的其他成员使用。
4.为了实现应用程序的国际化,往往需要使用动态连接库。使用动态连接库可以将针对某一国家、语言的信息存放在其中。对于不同的版本,使用不同的动态连接库。在使用AppWizard生成应用程序时,我们可以指定资源文件使用的语言,这就是通过提供不同的动态连接库实现的。
VC++、C++ Builder、Delphi都可以编写DLL文件。Visual Basic 5.0以上版本也可以编写一种特殊的DLL,即ActiveX DLL。
DLL不是独立运行的程序,它是某个程序的一个部分,它只能由所属的程序调用。用户不能,也不需要打开它。
在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库,即DLL文件,放置于系统中。当我们执行某一个程序时,相应的DLL文件就会被调用。一个应用程序可有多个DLL文件,一个DLL文件也可能被几个应用程序所共用,这样的DLL文件被称为共享DLL文件。DLL文件一般被存放在C:WindowsSystem目录下。
二、修改DLL文件的具体应用
在系统的组策略和注册表中,我们可以修改一些键值来优化我们的系统,并加强操作系统的安全性。可是,对于限制下载、禁止删除文件等功能,我们无法通过上述的操作来完成,这只有通过修改系统DLL文件来实现。
目前,我们通过修改系统的DLL文件,可以实现禁止删除文件、禁止IE下载、禁止IE另存为、禁止文件打开方式等功能。
三、系统中部分DLL文件的功能
1、Browselc.dll IE所需要调用的库文件DLL 结构雏形就是它了
2、Shdoclc.dll 系统窗口及设置等,如删除文件、重命名
3、Shell32.dll 系统窗口及设置等,如删除文件、重命名
4、Cryptui.dll IE控件下载及提示对话框程序
四、修改DLL文件的方法
1、下载DLL文件修改工具EXESCOPE6.3 - 6.4工具
2、获取Browselc.dll、Shdoclc.dll、Shell32.dll和Cryptui.dll这几个链接文件。在找这几个文件时,最好将其他机器的硬盘,挂接在本机中,然后用本机的操作系统启动并复制这几个文件。
3、在修改DLL文件的时候,打开该键值,在右面的对话框中将所要修改的键值禁用即可,不要删除,以备日后恢复(如图)

五、DLL文件修改秘籍
1、禁止下载的修改方法:
打开 Shdoclc.dll 修改资源--对话框---4416,将4416键值禁用即可。
2、禁止网页添加到收藏夹,打开Shdoclc.dll 修改资源--对话框---21400,将该键值禁用即可。
3、禁止恶意网页加载控件,修改Cryptui.dll文件,要同时修改5个地方才能完全禁止
资源--对话框---130
资源--对话框---230
资源--对话框---4101
资源--对话框---4104
资源--对话框---4107
将以各对话框中的相应键值,修改成为禁用就可以了。
4、禁止系统删除文件修改Shell32.dll,这个文件需要修改5个地方才可以禁止系统删除文件。
资源--对话框---1011
资源--对话框---1012
资源--对话框---1013
资源--对话框---1021
资源--对话框---1022
将以上五个地址的键值禁用就可以了!
5、禁止文件被改名,修改shell32.dll,有2个地方需要修改
资源--对话框---1018
资源--对话框---1019
将以上两处的相应键值,修改为禁用就可以了!
6、禁止运行菜单,修改shell32.dll,将资源--对话框---1018键值设置为禁用。
7、禁止系统文件被挪动修改shell32.dll,需要修改4个地方
资源--对话框---1014
资源--对话框---1015
资源--对话框---1016
资源--对话框---1017
8、禁止目标另存为,修改 Shdoclc.dll 文件,需要修改3个地方
资源--菜单--258---257
资源--菜单--258---252
资源--菜单--24641--2268
在这个修改中,我们要把各对应的键值删除。打开该键值后,右键菜单中有删除。在资源--菜单--24641—2268中,有多项该键值,请逐一删除。
9、禁止自定义文件夹选项修改Shell32.dll 文件,需要修改以下4个地方
资源--菜单--215---28719
资源--菜单--216---28719
资源--菜单--217---28719
资源--菜单--216---28719
找到以上四处键值,直接需要删除后即可,而不是禁用。
10、禁止IE文件夹选项,修改 Browselc.dll 文件,需要修改3个键值
资源--菜单--263 (这里有多个请删除)---41251(删除)
资源--菜单--266( 也有多个请删除)---41329 (删除)
资源--菜单--268---41251 (删除)
在上面的3个键值中,个别键值有多处,请逐一删除。
11、禁止98 文件共享控件,修改 Msshrui.dll,需要修改2个地方
资源--- 对话框---- 1 --- AutoRadioButton
资源--- 对话框---- 30 --- AutoRadioButton
将以上两处的键值禁用即可。其他的选项,可以根据自己的需要进行修改。找到相应的功能键值,将不需要的功能,禁用就可以了。
12、禁止文件的打开方式,修改 Url.dll,需要修改2个地方
资源--- 对话框--- 7000
资源--- 对话框--- 7005
将以上两处的键值禁用即可。
13、禁止更改系统桌面,修改 Shdoc401.dll,有2处地方需要修改
资源--- 对话框--- 29952--- PushButton:浏览
资源--- 对话框--- 29952--- PushButton:图案
将以上两处的键值禁用即可。
14、禁止系统文件夹自定义,修改 Shd401lc.dll,有2处地方需要修改
资源--- 对话框--- 29957
资源--- 对话框--- 29958
将以上两处的键值禁用即可。
15、禁止文件保存路径及打开,修改 Comdlg32.dll,有2处地方需要修改
资源--- 对话框--- 1547
资源--- 对话框--- 1548
将以上两处的键值禁用即可。
六、注意事项
1、本文以Windows XP/2000操作系统的DLL文件修改为例,并不一定适用于Windows 98操作系统,请在修改DLL文件时注意。
2、在禁止下载的操作中,除修改禁止下载的链接文件Shdoclc.dll外,还要修改禁止文件另存为的链接文件Shdoclc.dll。如果修改一个链接文件无法实现相应的功能,请查看其他链接文件中是否存在需要修改的键值。
3、修改后的DLL链接文件,需要在DOS模式下导入并重新启动机器才有效。
4、本文章只介绍了我们日常操作中经常用到的DLL修改选项,其他选项请参看DLL文件中的具体选项进行修改。
5、在修改DLL文件前,请先备份系统原有的DLL文件,以备修改失败恢复。
CSP封装及加密服务提供程序
CSP(Chip Scale Package),是芯片级封装的意思。CSP封装最新一代的内存芯片封装技术,其技术性能又有了新的提升。CSP封装可以让芯片面积与封装面积之比超过1:1.14,已经相当接近1:1的理想情况,绝对尺寸也仅有32平方毫米,约为普通的BGA的1/3,仅仅相当于TSOP内存芯片面积的1/6。与BGA封装相比,同等空间下CSP封装可以将存储容量提高三倍。 CSP封装内存不但体积小,同时也更薄,其金属基板到散热体的最有效散热路径仅有0.2毫米,大大提高了内存芯片在长时间运行后的可靠性,线路阻抗显著减小,芯片速度也随之得到大幅度提高。 CSP封装内存芯片的中心引脚形式有效地缩短了信号的传导距离,其衰减随之减少,芯片的抗干扰、抗噪性能也能得到大幅提升,这也使得CSP的存取时间比BGA改善15%-20%。在CSP的封装方式中,内存颗粒是通过一个个锡球焊接在PCB板上,由于焊点和PCB板的接触面积较大,所以内存芯片在运行中所产生的热量可以很容易地传导到PCB板上并散发出去。CSP封装可以从背面散热,且热效率良好,CSP的热阻为35℃/W,而TSOP热阻40℃/W。
|
加密服务提供程序(Cryptographic Service Provider,CSP),其中,CSP 负责创建密钥、吊销密钥,以及使用密钥执行各种加、解密操作。每个 CSP 都提供了不同的实现方式。某些提供了更强大的加密算法,而另一些则包含硬件组件,例如智能 IC 卡或 USB 电子令牌。 当读者使用特别的数字证书存储介质(如:智能 IC 卡或 USB 电子令牌)存储数字证书及其相应的私有密钥时,可以在“加密服务提供程序(CSP)”下拉框中选择该存储介质生产厂商提供的CSP。
MD5时代渐行渐远(转)
2004年8月17日的美国加州圣巴巴拉,国际密码学年会(Crypto 2004)上,来自中国山东大学王小云教授的一篇关于"破译MD5、HAVAL-128、MD4以及RIPEMD-128算法"的报告引起了轰动,报告中提到的新破译方法几乎标志着世界通信密码标准——MD5堡垒的轰然倒塌。一石激起千层浪,此前一直负责公开征集针对MD5的攻击而设立的权威站点http//www.md5crk.com/宣布"由于MD5破译获得突破性进展,MD5破解项目(MD5CRK Project)即日停止",并开始提供该站点以往技术资料的下载,预计该站点也将在不久的将来完全关闭。面对MD5被破译,有人一声叹息,有人觉得不可思议,更有人忧虑甚至恐慌......那么究竟MD5有什么来头?它被破译是否意味着"地球将不再旋转"?谁将成为它的继承者?请看——
一、MD5是何方神圣?
所谓MD5,即"Message-Digest Algorithm 5(信息-摘要算法)",它由MD2、MD3、MD4发展而来的一种单向函数算法(也就是HASH算法),它是国际著名的公钥加密算法标准RSA的第一设计者R.Rivest于上个世纪90年代初开发出来的。MD5的最大作用在于,将不同格式的大容量文件信息在用数字签名软件来签署私人密钥前"压缩"成一种保密的格式,关键之处在于——这种"压缩"是不可逆的。
为了让读者朋友对MD5的应用有个直观的认识,笔者以一个比方和一个实例来简要描述一下其工作过程:
大家都知道,地球上任何人都有自己独一无二的指纹,这常常成为公安机关鉴别罪犯身份最值得信赖的方法;与之类似,MD5就可以为任何文件(不管其大小、格式、数量)产生一个同样独一无二的"数字指纹",如果任何人对文件做了任何改动,其MD5值也就是对应的"数字指纹"都会发生变化。
我们常常在某些软件下载站点的某软件信息中看到其MD5值,它的作用就在于我们可以在下载该软件后,对下载回来的文件用专门的软件(如Windows MD5 Check等)做一次MD5校验,以确保我们获得的文件与该站点提供的文件为同一文件。利用MD5算法来进行文件校验的方案被大量应用到软件下载站、论坛数据库、系统文件安全等方面。
笔者上面提到的例子只是MD5的一个基本应用,实际上MD5还被用于加密解密技术上,如Unix、各类BSD系统登录密码(在MD5诞生前采用的是DES加密算法,后因MD5安全性更高,DES被淘汰)、通信信息加密(如大家熟悉的即时通信软件MyIM)、数字签名等诸多方面。
二、MD5的消亡之路
实际上,从MD5诞生之日起,来自美国名为Van Oorschot和Wiener的两位密码学专家就发现了一个暴力搜寻冲突的函数,并预算出"使用一个专门用来搜索MD5冲突的机器可以平均每24天就找到一个冲突"。不过由于该方案仅仅从理论上证明了MD5的不安全性,且实现的代价及其夸张(当时要制造这种专门的计算机,成本需要100万美元),于是MD5自其诞生十多年来一直未有新版本或者被其它算法彻底取代。
在接下来的日子里,有关MD5的破译又诞生了"野蛮攻击",也就是用"穷举法"从所有可能产生的结果中找到被MD5加密的原始明文,不过由于MD5采用128位加密方法,即使一台机器每秒尝试10亿条明文,那么要破译出原始明文大概需要10的22次方年,而一款名为"MD5爆破工具"的软件,每秒进行的运算仅仅为2万次!
经过无数MD5算法研究专家的努力,先后又诞生了"生日攻击"、"微分攻击"等多种破译方法(相关信息大家可以参考研究成果,大大推进了md5算法消亡的进程。尽管在研究报告中并没有提及具体的实现方法,我们可以认为,md5被彻底攻破已经扫除了技术上的障碍,剩下的仅仅是时间和精力上的问题。/" target=_blank>http://www.md5crk.com)。此次山东大学几位教授的最新研究成果,大大推进了MD5算法消亡的进程。尽管在研究报告中并没有提及具体的实现方法,我们可以认为,MD5被彻底攻破已经扫除了技术上的障碍,剩下的仅仅是时间和精力上的问题。
三、MD5完蛋了,放在银行的存款还安全吗?
由于MD5应用极其广泛,即使是在银行数字签名证书中,它依然占据着比较重要的地位,此次MD5被成功破译的新闻让不少不明所以的人感到"恐惧",认为这是对整个密码界的彻底颠覆,甚至有人开始担心"自己放在银行或者网络银行账户中的存款也有被盗取的可能"。
其实这种忧虑完全是杞人忧天,以目前主流的网络银行的加密技术为例,它们都构建于PKI(Pubic Key Infrastructure,公钥加密技术)平台之上,与公钥成对的私钥只掌握在与之通信的另一方,这一"信任关系"是通过公钥证书来实现的。PKI的整个安全体系由加密、数字签名、数据完整性机制等技术来共同保障,其密码算法包括对称密码算法(如DES、3DES)、公开密钥密码算法(如ECC、RSA),即使在同样有应用的HASH算法方面,目前网络银行所采用的大多是SHA-1算法,该算法与MD5的128位加密相比,使用了160位加密方式,比MD5安全性高不少。
其实,就目前网络银行的安全隐患来看,更多的是来自客户接入端(如Web入口),而非银行的加密技术本身。
四、MD5的继承者们
"天下没有不透风的墙",实际上任何一种算法都会有其漏洞,即使是目前大行其道的MD5和SHA-1,当对漏洞的研究发展到其能够被有效利用时,则标志着该算法灭亡的时候到了。所谓"天下无不散之筵席",MD5逐渐退出历史舞台后,下一个接任者又会是谁呢?
实际上,长期以来,密码界一直在致力于对新加密算法的研究,而且在高度机密的安全领域,所采用的加密算法也绝非MD5,各国政府、各大公司都在研究拥有独立技术的加密算法,其中比较出色的代表有SHA-1、SHA-224等。此次MD5破译报告发表后,美国国家技术与标准局(NIST)表示,鉴于MD5被破译以及SHA-1漏洞被发现,他们将逐渐放弃目前使用的SHA-1,于2010年前逐步推广更安全的SHA-224、SHA-256、SHA-384和SHA-512。这些算法与MD5的128位加密相比,加密位数和安全性能都提高了很多倍。
尽管MD5被淘汰已经成为必然,不过鉴于它开源以及免费的特性,而且目前还没有真正有效的快速破解方法,因此它还将继续在历史舞台活跃一段时间。
2004年8月17日的美国加州圣巴巴拉,正在召开的国际密码学会议(Crypto’2004)安排了三场关于杂凑函数的特别报告。在国际著名密码学家Eli Biham和Antoine Joux相继做了对SHA-1的分析与给出SHA-0的一个碰撞之后,来自山东大学的王小云教授做了破译MD5、HAVAL-128、 MD4和RIPEMD算法的报告。在会场上,当她公布了MD系列算法的破解结果之后,报告被激动的掌声打断。王小云教授的报告轰动了全场,得到了与会专家的赞叹。报告结束时,与会者长时间热烈鼓掌,部分学者起立鼓掌致敬,这在密码学会议上是少见的盛况。王小云教授的报告缘何引起如此大的反响?因为她的研究成果作为密码学领域的重大发现宣告了固若金汤的世界通行密码标准MD5的堡垒轰然倒塌,引发了密码学界的轩然大波。会议总结报告这样写道:“我们该怎么办?MD5被重创了;它即将从应用中淘汰。SHA-1仍然活着,但也见到了它的末日。现在就得开始更换SHA-1了。”
关键词:碰撞=漏洞=别人可以伪造和冒用数字签名。
Hash函数与数字签名(数字手印)
HASH函数,又称杂凑函数,是在信息安全领域有广泛和重要应用的密码算法,它有一种类似于指纹的应用。在网络安全协议中,杂凑函数用来处理电子签名,将冗长的签名文件压缩为一段独特的数字信息,像指纹鉴别身份一样保证原来数字签名文件的合法性和安全性。在前面提到的SHA-1和MD5都是目前最常用的杂凑函数。经过这些算法的处理,原始信息即使只更动一个字母,对应的压缩信息也会变为截然不同的“指纹”,这就保证了经过处理信息的唯一性。为电子商务等提供了数字认证的可能性。
安全的杂凑函数在设计时必须满足两个要求:其一是寻找两个输入得到相同的输出值在计算上是不可行的,这就是我们通常所说的抗碰撞的;其二是找一个输入,能得到给定的输出在计算上是不可行的,即不可从结果推导出它的初始状态。现在使用的重要计算机安全协议,如SSL,PGP都用杂凑函数来进行签名,一旦找到两个文件可以产生相同的压缩值,就可以伪造签名,给网络安全领域带来巨大隐患。
MD5就是这样一个在国内外有着广泛的应用的杂凑函数算法,它曾一度被认为是非常安全的。然而,王小云教授发现,可以很快的找到MD5的“碰撞”,就是两个文件可以产生相同的“指纹”。这意味着,当你在网络上使用电子签名签署一份合同后,还可能找到另外一份具有相同签名但内容迥异的合同,这样两份合同的真伪性便无从辨别。王小云教授的研究成果证实了利用MD5算法的碰撞可以严重威胁信息系统安全,这一发现使目前电子签名的法律效力和技术体系受到挑战。因此,业界专家普林斯顿计算机教授Edward Felten等强烈呼吁信息系统的设计者尽快更换签名算法,而且他们强调这是一个需要立即解决的问题。
国际讲坛 王氏发现艳惊四座
面对Hash函数领域取得的重大研究进展,Crypto 2004 会议总主席StorageTek高级研究员Jim Hughes 17 日早晨表示,此消息太重要了,因此他已筹办该会成立24年来的首次网络广播(Webcast )。Hughes在会议上宣布:“会中将提出三份探讨杂凑碰撞(hash collisions )重要的研究报告。”其中一份是王小云等几位中国研究人员的研究发现。17日晚,王小云教授在会上把他们的研究成果做了宣读。这篇由王小云、冯登国、来学嘉、于红波四人共同完成的文章,囊括了对MD5、HAVAL-128、 MD4和RIPEMD四个著名HASH算法的破译结果。在王小云教授仅公布到他们的第三个惊人成果的时候,会场上已经是掌声四起,报告不得不一度中断。报告结束后,所有与会专家对他们的突出工作报以长时的热烈掌声,有些学者甚至起立鼓掌以示他们的祝贺和敬佩。当人们掌声渐息,来学嘉教授又对文章进行了一点颇有趣味的补充说明。由于版本问题,作者在提交会议论文时使用的一组常数和先行标准不同;在会议发现这一问题之后,王小云教授立即改变了那个常数,在很短的时间内就完成了新的数据分析,这段有惊无险的小插曲倒更加证明了他们论文的信服力,攻击方法的有效性,反而凸显了研究工作的成功。
会议结束时,很多专家围拢到王小云教授身边,既有简短的探讨,又有由衷的祝贺,褒誉之词不绝。包含公钥密码的主要创始人R. L. Rivest和A. Shamir在内的世界顶级的密码学专家也上前表示他们的欣喜和祝贺。
国际密码学专家对王小云教授等人的论文给予高度评价。
MD5的设计者,同时也是国际著名的公钥加密算法标准RSA的第一设计者R.Rivest在邮件中写道:“这些结果无疑给人非常深刻的印象,她应当得到我最热烈的祝贺,当然,我并不希望看到MD5就这样倒下,但人必须尊崇真理。”
Francois Grieu这样说:“王小云、冯登国、来学嘉和于红波的最新成果表明他们已经成功破译了MD4、MD5、HAVAL-128、RIPEMD-128。并且有望以更低的复杂度完成对SHA-0的攻击。一些初步的问题已经解决。他们赢得了非常热烈的掌声。”
另一位专家Greg Rose如此评价:“我刚刚听了Joux和王小云的报告,王所使用的技术能在任何初始值下用2^40次hash运算找出SHA-0的碰撞。她在报告中对四种HASH函数都给出了碰撞,她赢得了长时间的起立喝彩,(这在我印象中还是第一次)。…… 她是当今密码学界的巾帼英雄。……(王小云教授的工作)技术虽然没有公开,但结果是无庸质疑的,这种技术确实存在。…… 我坐在Ron Rivest前面,我听到他评论道:‘我们不得不做很多的重新思考了。’”
石破惊天 MD5堡垒轰然倒塌
一石击起千层浪,MD5的破译引起了密码学界的激烈反响。专家称这是密码学界近年来“最具实质性的研究进展”,各个密码学相关网站竞相报导这一惊人突破。
MD5破解专项网站关闭
MD5破解工程权威网站http://www.md5crk.com/ 是为了公开征集专门针对MD5的攻击而设立的,网站于2004年8月17日宣布:“中国研究人员发现了完整MD5算法的碰撞;Wang, Feng, Lai与Yu公布了MD5、MD4、HAVAL-128、RIPEMD-128几个 Hash函数的碰撞。这是近年来密码学领域最具实质性的研究进展。使用他们的技术,在数个小时内就可以找到MD5碰撞。……由于这个里程碑式的发现,MD5CRK项目将在随后48小时内结束”。
对此,http://www.readyresponse.org主页专门转载了该报道http://www.aspenleaf.com/distributed/distrib-recent.html和几个其它网站也进行了报道。
权威网站相继发表评论或者报告这一重大研究成果
经过统计,在论文发布两周之内,已经有近400个网站发布、引用和评论了这一成果。国内的许多新闻网站也以“演算法安全加密功能露出破绽 密码学界一片哗然”为题报道了这一密码学界的重大事件。(报导见http://www.technewsworld.com/per ... nded&mview=flat,该消息在各新闻网站上多次转载。)
东方神韵 MD5终结者来自中国
MD5破解工作的主要成员王小云教授是一个瘦弱、矜持的女子,厚厚的镜片透射出双眸中数学的灵光。她于1990年在山东大学师从著名数学家潘承洞教授攻读数论与密码学专业博士,在潘先生、于秀源、展涛等多位著名教授的悉心指导下,她成功将数论知识应用到密码学中,取得了很多突出成果,先后获得863项目资助和国家自然科学基金项目资助,并且获得部级科技进步奖一项,撰写论文二十多篇。王小云教授从上世纪90年代末开始进行HASH函数的研究,她所带领的于红波、王美琴、孙秋梅、冯骐等组成的密码研究小组,同中科院冯登国教授,上海交大来学嘉等知名学者密切协作,经过长期坚持不懈的努力,找到了破解HASH函数的关键技术,成功的破解了MD5和其它几个HASH函数。
近年来她的工作得到了山东大学和数学院领导的大力支持,特别投资建设了信息安全实验室。山东大学校长展涛教授高度重视王小云教授突出的科研成果。 2004年6月山东大学领导听取王小云教授的工作介绍后,展涛校长亲自签发邀请函邀请国内知名信息安全专家参加2004年7月在威海举办的“山东大学信息安全研究学术研讨会”,数学院院长刘建亚教授组织和主持了会议,会上王小云教授公布了MD5等算法的一系列研究成果,专家们对她的研究成果给予了充分的肯定,对其坚持不懈的科研态度大加赞扬。一位院士说,她的研究水平绝对不比国际上的差。这位院士的结论在时隔一个月之后的国际密码会上得到了验证,国外专家如此强烈的反响表明,我们的工作可以说不但不比国际上的差,而且是在破解HASH函数方面已领先一步。加拿大CertainKey公司早前宣布将给予发现MD5算法第一个碰撞人员一定的奖励,CertainKey的初衷是利用并行计算机通过生日攻击来寻找碰撞,而王小云教授等的攻击相对生日攻击需要更少的计算时间。
数字认证 你的未来不是梦
由于MD5的破译,引发了关于MD5产品是否还能够使用的大辩论。在麻省理工大学Jeffrey I. Schiller教授主持的个人论坛上,许多密码学家在标题为“Bad day at the hash function factory”的辩论中发表了具有价值的意见(http://jis.mit.edu/pipermail/saag/2004q3/000913.html)。这次国际密码学会议的总主席Jimes Hughes发表评论说“我相信这(破解MD5)是真的,并且如果碰撞存在,HMAC也就不再是安全的了,…… 我认为我们应该抛开MD5了。” Hughes建议,程序设计人员最好开始舍弃MD5。他说:“既然现在这种算法的弱点已暴露出来,在有效的攻击发动之前,现在是撤离的时机。”
同样,在普林斯顿大学教授Edwards Felton的个人网站(http://www.freedom-to-tinker.com/archives/000664.html)上,也有类似的评论。他说:“留给我们的是什么呢?MD5已经受了重伤;它的应用就要淘汰。SHA-1仍然活着,但也不会很长,必须立即更换SHA-1,但是选用什么样的算法,这需要在密码研究人员达到共识。”
密码学家Markku-Juhani称“这是HASH函数分析领域激动人心的时刻。(http://www.tcs.hut.fi/~mjos/md5/)”
而著名计算机公司SUN的LINUIX专家Val Henson则说:“以前我们说"SHA-1可以放心用,其他的不是不安全就是未知", 现在我们只能这么总结了:"SHA-1不安全,其他的都完了"。
针对王小云教授等破译的以MD5为代表的Hash函数算法的报告,美国国家技术与标准局(NIST)于2004年8月24日发表专门评论,评论的主要内容为:“在最近的国际密码学会议(Crypto 2004)上,研究人员宣布他们发现了破解数种HASH算法的方法,其中包括MD4,MD5,HAVAL-128,RIPEMD还有 SHA-0。分析表明,于1994年替代SHA-0成为联邦信息处理标准的SHA-1的减弱条件的变种算法能够被破解;但完整的SHA-1并没有被破解,也没有找到SHA-1的碰撞。研究结果说明SHA-1的安全性暂时没有问题,但随着技术的发展,技术与标准局计划在2010年之前逐步淘汰SHA-1,换用其他更长更安全的算法(如SHA-224、SHA-256、SHA-384和SHA-512)来替代。”
详细评论见:http://csrc.nist.gov/hash_standards_comments.pdf
2004年8月28日,十届全国人大常委会第十一次会议表决通过了电子签名法。这部法律规定,可靠的电子签名与手写签名或者盖章具有同等的法律效力。电子签名法的通过,标志着我国首部“真正意义上的信息化法律”已正式诞生,将于2005年4月1日起施行。专家认为,这部法律将对我国电子商务、电子政务的发展起到极其重要的促进作用。王小云教授的发现无异于发现了信息化天空的一个惊人黑洞。我们期待着王小云教授和她的团队能够成就“女娲补天”的壮举,为人类的信息化之路保驾护航。
Linux系统调用讲义
操作系统是从硬件抽象出来的虚拟机,在该虚拟机上用户可以运行应用程序。它负责直接与硬件交互,向用户程序提供公共服务,并使它们同硬件特性隔离。因为程序不应该依赖于下层的硬件,只有这样应用程序才能很方便的在各种不同的Unix系统之间移动。系统调用是Unix/Linux操作系统向用户程序提供支持的接口,通过这些接口应用程序向操作系统请求服务,控制转向操作系统,而操作系统在完成服务后,将控制和结果返回给用户程序。
一个Unix/Linux系统分为三个层次:用户、核心以及硬件。
其中系统调用是用户程序与核心间的边界,通过系统调用进程可由用户模式转入核心模式,在核心模式下完成一定的服务请求后在返回用户模式。
系统调用接口看起来和C程序中的普通函数调用很相似,它们通常是通过库把这些函数调用映射成进入操作系统所需要的原语。
这些操作原语只是提供一个基本功能集,而通过库对这些操作的引用和封装,可以形成丰富而且强大的系统调用库。这里体现了机制与策略相分离的编程思想——系统调用只是提供访问核心的基本机制,而策略是通过系统调用库来体现。
例:execv, execl, execlv, opendir , readdir...
运行模式(运行态):
一种计算机硬件要运行Unix/Linux系统,至少需要提供两种运行模式:高优先级的核心模式和低优先级的用户模式。
实际上许多计算机都有两种以上的执行模式。如:intel 80x86体系结构就有四层执行特权,内层特权最高。Unix只需要两层即可以了:核心运行在高优先级,称之为核心态;其它外围软件包括shell,编辑程序,Xwindow等等都是在低优先级运行,称之为用户态。之所以采取不同的执行模式主要原因时为了保护,由于用户进程在较低的特权级上运行,它们将不能意外或故意的破坏其它进程或内核。程序造成的破坏会被局部化而不影响系统中其它活动或者进程。当用户进程需要完成特权模式下才能完成的某些功能时,必须严格按照系统调用提供接口才能进入特权模式,然后执行调用所提供的有限功能。
每种运行态都应该有自己的堆栈。在Linux中,分为用户栈和核心栈。用户栈包括在用户态执行时函数调用的参数、局部变量和其它数据结构。有些系统中专门为全局中断处理提供了中断栈,但是x86中并没有中断栈,中断在当前进程的核心栈中处理。
地址空间:
采用特权模式进行保护的根本目的是对地址空间的保护,用户进程不应该能够访问所有的地址空间:只有通过系统调用这种受严格限制的接口,进程才能进入核心态并访问到受保护的那一部分地址空间的数据,这一部分通常是留给操作系统使用。另外,进程与进程之间的地址空间也不应该随便互访。这样,就需要提供一种机制来在一片物理内存上实现同一进程不同地址空间上的保护,以及不同进程之间地址空间的保护。
Unix/Linux中通过虚存管理机制很好的实现了这种保护,在虚存系统中,进程所使用的地址不直接对应物理的存储单元。每个进程都有自己的虚存空间,每个进程有自己的虚拟地址空间,对虚拟地址的引用通过地址转换机制转换成为物理地址的引用。正因为所有进程共享物理内存资源,所以必须通过一定的方法来保护这种共享资源,通过虚存系统很好的实现了这种保护:每个进程的地址空间通过地址转换机制映射到不同的物理存储页面上,这样就保证了进程只能访问自己的地址空间所对应的页面而不能访问或修改其它进程的地址空间对应的页面。
虚拟地址空间分为两个部分:用户空间和系统空间。在用户模式下只能访问用户空间而在核心模式下可以访问系统空间和用户空间。系统空间在每个进程的虚拟地址空间中都是固定的,而且由于系统中只有一个内核实例在运行,因此所有进程都映射到单一内核地址空间。内核中维护全局数据结构和每个进程的一些对象信息,后者包括的信息使得内核可以访问任何进程的地址空间。通过地址转换机制进程可以直接访问当前进程的地址空间(通过MMU),而通过一些特殊的方法也可以访问到其它进程的地址空间。
尽管所有进程都共享内核,但是系统空间是受保护的,进程在用户态无法访问。进程如果需要访问内核,则必须通过系统调用接口。进程调用一个系统调用时,通过执行一组特殊的指令(这个指令是与平台相关的,每种系统都提供了专门的trap命令,基于x86的Linux中是使用int 指令)使系统进入内核态,并将控制权交给内核,由内核替代进程完成操作。当系统调用完成后,内核执行另一组特征指令将系统返回到用户态,控制权返回给进程。
上下文:
一个进程的上下文可以分为三个部分:用户级上下文、寄存器上下文以及系统级上下文。
用户级上下文:正文、数据、用户栈以及共享存储区;
寄存器上下文:程序寄存器(IP),即CPU将执行的下条指令地址,处理机状态寄存器(EFLAGS),栈指针,通用寄存器;
系统级上下文:进程表项(proc结构)和U区,在Linux中这两个部分被合成task_struct,区表及页表(mm_struct , vm_area_struct, pgd, pmd, pte等),核心栈等。
全部的上下文信息组成了一个进程的运行环境。当发生进程调度时,必须对全部上下文信息进行切换,新调度的进程才能运行。进程就是上下文的集合的一个抽象概念。
系统调用可以看作是一个所有Unix/Linux进程共享的子程序库,但是它是在特权方式下运行,可以存取核心数据结构和它所支持的用户级数据。系统调用的主要功能是使用户可以使用操作系统提供的有关设备管理、文件系统、进程控制进程通讯以及存储管理方面的功能,而不必要了解操作系统的内部结构和有关硬件的细节问题,从而减轻用户负担和保护系统以及提高资源利用率。
系统调用分为两个部分:与文件子系统交互的和进程子系统交互的两个部分。其中和文件子系统交互的部分进一步由可以包括与设备文件的交互和与普通文件的交互的系统调用(open, close, ioctl, create, unlink, . . . );与进程相关的系统调用又包括进程控制系统调用(fork, exit, getpid, . . . ),进程间通讯,存储管理,进程调度等方面的系统调用。
在每种平台上,都有特定的指令可以使进程的执行由用户态转换为核心态,这种指令称作操作系统陷入(operating system trap)。进程通过执行陷入指令后,便可以在核心态运行系统调用代码。
在Linux中是通过软中断来实现这种陷入的,在x86平台上,这条指令是int 0x80。也就是说在Linux中,系统调用的接口是一个中断处理函数的特例。具体怎样通过中断处理函数来实现系统调用的入口将在后面详细介绍。
这样,就需要在系统启动时,对INT 0x80进行一定的初始化,下面将描述其过程:
1.使用汇编子程序setup_idt(linux/arch/i386/kernel/head.S)初始化idt表(中断描述符表),这时所有的入口函数偏移地址都被设为ignore_int
lea ignore_int,%edx
movl $(__KERNEL_CS << 16),%eax
movw %dx,%ax /* selector = 0x0010 = cs */
movw $0x8E00,%dx /* interrupt gate - dpl=0, present */
lea SYMBOL_NAME(idt_table),%edi
mov $256,%ecx
rp_sidt:
movl %eax,(%edi)
movl %edx,4(%edi)
addl $8,%edi
dec %ecx
jne rp_sidt
ret
selector = __KERNEL_CS, DPL = 0, TYPE = E, P = 1);
2.Start_kernel()(linux/init/main.c)调用trap_init()(linux/arch/i386/kernel/trap.c)函数设置中断描述符表。在该函数里,实际上是通过调用函数set_system_gate(SYSCALL_VECTOR,&system_call)来完成该项的设置的。其中的SYSCALL_VECTOR就是0x80,而system_call则是一个汇编子函数,它即是中断0x80的处理函数,主要完成两项工作:a. 寄存器上下文的保存;b. 跳转到系统调用处理函数。在后面会详细介绍这些内容。
set_system_gate()是在linux/arch/i386/kernel/trap.S中定义的,在该文件中还定义了几个类似的函数set_intr_gate(), set_trap_gate, set_call_gate()。这些函数都调用了同一个汇编子函数__set_gate(),该函数的作用是设置门描述符。IDT中的每一项都是一个门描述符。
#define _set_gate(gate_addr,type,dpl,addr)
set_gate(idt_table+n,15,3,addr);
门描述符的作用是用于控制转移,其中会包括选择子,这里总是为__KERNEL_CS(指向GDT中的一项段描述符)、入口函数偏移地址、门访问特权级(DPL)以及类型标识(TYPE)。Set_system_gate的DPL为3,表示从特权级3(最低特权级)也可以访问该门,type为15,表示为386中断门。)
1.系统调用处理函数的函数名的约定
函数名都以“sys_”开头,后面跟该系统调用的名字。例如,系统调用fork()的处理函数名是sys_fork()。
asmlinkage int sys_fork(struct pt_regs regs);
(补充关于asmlinkage的说明)
2.系统调用号(System Call Number)
核心中为每个系统调用定义了一个唯一的编号,这个编号的定义在linux/include/asm/unistd.h中,编号的定义方式如下所示:
#define __NR_exit 1
#define __NR_fork 2
#define __NR_read 3
#define __NR_write 4
. . . . . .
用户在调用一个系统调用时,系统调用号号作为参数传递给中断0x80,而该标号实际上是后面将要提到的系统调用表(sys_call_table)的下标,通过该值可以找到相映系统调用的处理函数地址。
3.系统调用表
ENTRY(sys_call_table)
.long SYMBOL_NAME(sys_exit)
.long SYMBOL_NAME(sys_fork)
.long SYMBOL_NAME(sys_read)
.long SYMBOL_NAME(sys_write)
. . . . . .
C.系统调用函数接口是如何转化为陷入命令
我们还是以x86为例说明:
由于陷入指令是一条特殊指令,而且依赖与操作系统实现的平台,如在x86中,这条指令是int 0x80,这显然不是用户在编程时应该使用的语句,因为这将使得用户程序难于移植。所以在操作系统的上层需要实现一个对应的系统调用库,每个系统调用都在该库中包含了一个入口点(如我们看到的fork, open, close等等),这些函数对程序员是可见的,而这些库函数的工作是以对应系统调用号作为参数,执行陷入指令int 0x80,以陷入核心执行真正的系统调用处理函数。当一个进程调用一个特定的系统调用库的入口点,正如同它调用任何函数一样,对于库函数也要创建一个栈帧。而当进程执行陷入指令时,它将处理机状态转换到核心态,并且在核心栈执行核心代码。
这里给出一个示例(linux/include/asm/unistd.h):
#define _syscallN(type, name, type1, arg1, type2, arg2, . . . ) \
type name(type1 arg1,type2 arg2) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2))); \
. . . . . .
__syscall_return(type,__res); \
}
在执行一个系统调用库中定义的系统调用入口函数时,实际执行的是类似如上的一段代码。这里牵涉到一些gcc的嵌入式汇编语言,不做详细的介绍,只简单说明其意义:
其中__NR_##name是系统调用号,如name == ioctl,则为__NR_ioctl,它将被放在寄存器eax中作为参数传递给中断0x80的处理函数。而系统调用的其它参数arg1, arg2, …则依次被放入ebx, ecx, . . .等通用寄存器中,并作为系统调用处理函数的参数,这些参数是怎样传入核心的将会在后面介绍。
下面将示例说明:
int func1()
{
int fd, retval;
fd = open(filename, ……);
……
ioctl(fd, cmd, arg);
. . .
}
func2()
{
int fd, retval;
fd = open(filename, ……);
……
__asm__ __volatile__(\
"int $0x80\n\t"\
:"=a"(retval)\
:"0"(__NR_ioctl),\
"b"(fd),\
"c"(cmd),\
"d"(arg));
}
这两个函数在Linux/x86上运行的结果应该是一样的。
若干个库函数可以映射到同一个系统调用入口点。系统调用入口点对每个系统调用定义其真正的语法和语义,但库函数通常提供一个更方便的接口。如系统调用exec有集中不同的调用方式:execl, execle,等,它们实际上只是同一系统调用的不同接口而已。对于这些调用,它们的库函数对它们各自的参数加以处理,来实现各自的特点,但是最终都被映射到同一个核心入口点。
D.系统调用陷入内核后作何初始化处理
在这一部分,我们将介绍INT 0x80的处理函数system_call。
思考一下就会发现,在调用前和调用后执行态完全不相同:前者是在用户栈上执行用户态程序,后者在核心栈上执行核心态代码。那么,为了保证在核心内部执行完系统调用后能够返回调用点继续执行用户代码,必须在进入核心态时保存时往核心中压入一个上下文层;在从核心返回时会弹出一个上下文层,这样用户进程就可以继续运行。
那么,这些上下文信息是怎样被保存的,被保存的又是那些上下文信息呢?这里仍以x86为例说明。
在执行INT指令时,实际完成了以下几条操作:
1.由于INT指令发生了不同优先级之间的控制转移,所以首先从TSS(任务状态段)中获取高优先级的核心堆栈信息(SS和ESP);2.把低优先级堆栈信息(SS和ESP)保留到高优先级堆栈(即核心栈)中;
3.把EFLAGS,外层CS,EIP推入高优先级堆栈(核心栈)中。
4.通过IDT加载CS,EIP(控制转移至中断处理函数)
#define SAVE_ALL \
pushl %es; \
pushl %ds; \
pushl %eax; \
pushl %ebp; \
pushl %edi; \
pushl %esi; \
pushl %edx; \
pushl %ecx; \
pushl %ebx; \
movl $(__KERNEL_DS),%edx; \
movl %edx,%ds; \
movl %edx,%es;
ENTRY(system_call)
SAVE_ALL
GET_CURRENT(%ebx)
cmpl $(NR_syscalls),%eax
jae badsys
testb $0x20,flags(%ebx) # PF_TRACESYS
jne tracesys
call *SYMBOL_NAME(sys_call_table)(,%eax,4)
在这里所做的所有工作是:
1.保存EAX寄存器,因为在SAVE_ALL中保存的EAX寄存器会被调用的返回值所覆盖;
2.调用SAVE_ALL保存寄存器上下文;
3.判断当前调用是否是合法系统调用(EAX是系统调用号,它应该小于NR_syscalls);
4.如果设置了PF_TRACESYS标志,则跳转到syscall_trace,在那里将会把当前进程挂起并向其父进程发送SIGTRAP,这主要是为了设 置调试断点而设计的;
5.如果没有设置PF_TRACESYS标志,则跳转到该系统调用的处理函数入口。这里是以EAX(即前面提到的系统调用号)作为偏移,在系 统调用表sys_call_table中查找处理函数入口地址,并跳转到该入口地址。
movl %esp, reg; \
andl $-8192, reg;
其作用是取得当前进程的task_struct结构的指针返回到reg中,因为在Linux中核心栈的位置是task_struct之后的两个页面处(8192bytes),所以此处把栈指针与-8192则得到的是task_struct结构指针,而task_struct中偏移为4的位置是成员flags,在这里指令testb $0x20,flags(%ebx)检测的就是task_struct->flags。
2.堆栈中的参数
正如前面提到的,SAVE_ALL是系统调用参数的传入过程,当执行完SAVE_ALL并且再由CALL指令调用其处理函数时,堆栈的结构应该如上图所示。这时的堆栈结构看起来和执行一个普通带参数的函数调用是一样的,参数在堆栈中对应的顺序是(arg1, ebx),(arg2, ecx),(arg3, edx). . . . . .,这正是SAVE_ALL压栈的反顺序,这些参数正是用户在使用系统调用时试图传送给核心的参数。下面是在核心的调用处理函数中使用参数的两种典型方法:
asmlinkage int sys_fork(struct pt_regs regs);
asmlinkage int sys_open(const char * filename, int flags, int mode);
在sys_fork中,把整个堆栈中的内容视为一个struct pt_regs类型的参数,该参数的结构和堆栈的结构是一致的,所以可以使用堆栈中的全部信息。而在sys_open中参数filename, flags, mode正好对应与堆栈中的ebx, ecx, edx的位置,而这些寄存器正是用户在通过C库调用系统调用时给这些参数指定的寄存器。
__asm__ __volatile__(\
"int $0x80\n\t"\
:"=a"(retval)\
:"0"(__NR_open),\
"b"(filename),\
"c"(flags),\
"d"(mode));
3.核心如何使用用户空间的参数
ENTRY(gdt_table)
.quad 0x0000000000000000/* not used */
.quad 0x00cf9a000000ffff /* 0x10 kernel 4GB code at 0x00000000 */
.quad 0x00cf92000000ffff /* 0x18 kernel 4GB data at 0x00000000 */
.quad 0x00cffa000000ffff /* 0x23 user 4GB code at 0x00000000 */
.quad 0x00cff2000000ffff /* 0x2b user 4GB data at 0x00000000 */
.quad 0x0000000000000000 /* not used */
.quad 0xc0c39a000000ffff /* 0x10 kernel 1GB code at 0xC0000000 */
.quad 0xc0c392000000ffff /* 0x18 kernel 1GB data at 0xC0000000 */
.quad 0x00cbfa000000ffff /* 0x23 user 3GB code at 0x00000000 */
.quad 0x00cbf2000000ffff /* 0x2b user 3GB data at 0x00000000 *
在2.0版的内核中SAVE_ALL宏定义还有这样几条语句:
"movl $" STR(KERNEL_DS) ",%edx\n\t" \
"mov %dx,%ds\n\t" \
"mov %dx,%es\n\t" \
"movl $" STR(USER_DS) ",%edx\n\t" \
"mov %dx,%fs\n\t" \
"movl $0,%edx\n\t" \
1.判断有没有软中断,如果有则跳转到软中断处理;
2.判断当前进程是否需要重新调度,如果需要则跳转到调度处理;
3.如果当前进程有挂起的信号还没有处理,则跳转到信号处理;
4.使用用RESTORE_ALL来弹出所有被SAVE_ALL压入核心栈的内容并且使用iret返回用户态。
F.实例介绍
这里实现的系统调用hello仅仅是在控制台上打印一条语句,没有任何功能。
1.修改linux/include/i386/unistd.h,在里面增加一条语句:
. . . . . .
asmlinkage int sys_hello(char * str)
{
printk(“My syscall: hello, I know what you say to me: %s ! \n”, str);
return 0;
}
4.修改linux/arch/i386/kernel/entry.S,在里面增加一条语句:
ENTRY(sys_call_table)
. . . . . .
.long SYMBOL_NAME(sys_hello)
并且修改:
.rept NR_syscalls-??? /* ??? = ??? +1 */
.long SYMBOL_NAME(sys_ni_syscall)
5.在linux/include/i386/中增加hello.h,里面至少应包括这样几条语句:
#ifdef __KERNEL
#else
inline _syscall1(int, hello, char *, str);
#endif
这样就可以使用系统调用hello了
进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合,这些资源在Linux中被抽象成各种数据对象:进程控制块、虚存空间、文件系统,文件I/O、信号处理函数。所以创建一个进程的过程就是这些数据对象的创建过程。
在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性,但是二者之间的通讯需要通过专门的通讯机制,如:pipe,fifo,System V IPC机制等,另外通过fork创建子进程系统开销很大,需要将上面描述的每种资源都复制一个副本。这样看来,fork是一个开销十分大的系统调用,这些开销并不是所有的情况下都是必须的,比如某进程fork出一个子进程后,其子进程仅仅是为了调用exec执行另一个执行文件,那么在fork过程中对于虚存空间的复制将是一个多余的过程(由于Linux中是采取了copy-on-write技术,所以这一步骤的所做的工作只是虚存管理部分的复制以及页表的创建,而并没有包括物理也面的拷贝);另外,有时一个进程中具有几个独立的计算单元,可以在相同的地址空间上基本无冲突进行运算,但是为了把这些计算单元分配到不同的处理器上,需要创建几个子进程,然后各个子进程分别计算最后通过一定的进程间通讯和同步机制把计算结果汇总,这样做往往有许多格外的开销,而且这种开销有时足以抵消并行计算带来的好处。
这说明了把计算单元抽象到进程上是不充分的,这也就是许多系统中都引入了线程的概念的原因。在讲述线程前首先介绍以下vfork系统调用,vfork系统调用不同于fork,用vfork创建的子进程共享地址空间,也就是说子进程完全运行在父进程的地址空间上,子进程对虚拟地址空间任何数据的修改同样为父进程所见。但是用vfork创建子进程后,父进程会被阻塞直到子进程调用exec或exit。这样的好处是在子进程被创建后仅仅是为了调用exec执行另一个程序时,因为它就不会对父进程的地址空间有任何引用,所以对地址空间的复制是多余的,通过vfork可以减少不必要的开销。
在Linux中, fork和vfork都是调用同一个核心函数
do_fork(unsigned long clone_flag, unsigned long usp, struct pt_regs)
其中clone_flag包括CLONE_VM, CLONE_FS, CLONE_FILES, CLONE_SIGHAND, CLONE_PID,CLONE_VFORK等等标志位,任何一位被置1了则表明创建的子进程和父进程共享该位对应的资源。所以在vfork的实现中,cloneflags = CLONE_VFORK | CLONE_VM | SIGCHLD,这表示子进程和父进程共享地址空间,同时do_fork会检查CLONE_VFORK,如果该位被置1了,子进程会把父进程的地址空间锁住,直到子进程退出或执行exec时才释放该锁。
在讲述clone系统调用前先简单介绍线程的一些概念。
线程是在进程的基础上进一步的抽象,也就是说一个进程分为两个部分:线程集合和资源集合。线程是进程中的一个动态对象,它应该是一组独立的指令流,进程中的所有线程将共享进程里的资源。但是线程应该有自己的私有对象:比如程序计数器、堆栈和寄存器上下文。
线程分为三种类型:
内核线程、轻量级进程和用户线程。
内核线程:
它的创建和撤消是由内核的内部需求来决定的,用来负责执行一个指定的函数,一个内核线程不需要和一个用户进程联系起来。它共享内核的正文段核全局数据,具有自己的内核堆栈。它能够单独的被调度并且使用标准的内核同步机制,可以被单独的分配到一个处理器上运行。内核线程的调度由于不需要经过态的转换并进行地址空间的重新映射,因此在内核线程间做上下文切换比在进程间做上下文切换快得多。
轻量级进程:
轻量级进程是核心支持的用户线程,它在一个单独的进程中提供多线程控制。这些轻量级进程被单独的调度,可以在多个处理器上运行,每一个轻量级进程都被绑定在一个内核线程上,而且在它的生命周期这种绑定都是有效的。轻量级进程被独立调度并且共享地址空间和进程中的其它资源,但是每个LWP都应该有自己的程序计数器、寄存器集合、核心栈和用户栈。
用户线程:
用户线程是通过线程库实现的。它们可以在没有内核参与下创建、释放和管理。线程库提供了同步和调度的方法。这样进程可以使用大量的线程而不消耗内核资源,而且省去大量的系统开销。用户线程的实现是可能的,因为用户线程的上下文可以在没有内核干预的情况下保存和恢复。每个用户线程都可以有自己的用户堆栈,一块用来保存用户级寄存器上下文以及如信号屏蔽等状态信息的内存区。库通过保存当前线程的堆栈和寄存器内容载入新调度线程的那些内容来实现用户线程之间的调度和上下文切换。
内核仍然负责进程的切换,因为只有内核具有修改内存管理寄存器的权力。用户线程不是真正的调度实体,内核对它们一无所知,而只是调度用户线程下的进程或者轻量级进程,这些进程再通过线程库函数来调度它们的线程。当一个进程被抢占时,它的所有用户线程都被抢占,当一个用户线程被阻塞时,它会阻塞下面的轻量级进程,如果进程只有一个轻量级进程,则它的所有用户线程都会被阻塞。
明确了这些概念后,来讲述Linux的线程和clone系统调用。
在许多实现了MT的操作系统中(如:Solaris,Digital Unix等), 线程和进程通过两种数据结构来抽象表示: 进程表项和线程表项,一个进程表项可以指向若干个线程表项, 调度器在进程的时间片内再调度线程。 但是在Linux中没有做这种区分, 而是统一使用task_struct来管理所有进程/线程,只是线程与线程之间的资源是共享的,这些资源可是是前面提到过的:虚存、文件系统、文件I/O以及信号处理函数甚至PID中的几种。
也就是说Linux中,每个线程都有一个task_struct,所以线程和进程可以使用同一调度器调度。其实Linux核心中,轻量级进程和进程没有质上的差别,因为Linux中进程的概念已经被抽象成了计算状态加资源的集合,这些资源在进程间可以共享。如果一个task独占所有的资源,则是一个HWP,如果一个task和其它task共享部分资源,则是LWP。
clone系统调用就是一个创建轻量级进程的系统调用:
int clone(int (*fn)(void * arg), void *stack, int flags, void * arg);
其中fn是轻量级进程所执行的过程,stack是轻量级进程所使用的堆栈,flags可以是前面提到的CLONE_VM, CLONE_FS, CLONE_FILES, CLONE_SIGHAND,CLONE_PID的组合。Clone 和fork,vfork在实现时都是调用核心函数do_fork。
do_fork(unsigned long clone_flag, unsigned long usp, struct pt_regs);
和fork、vfork不同的是,fork时clone_flag = SIGCHLD;
vfork时clone_flag = CLONE_VM | CLONE_VFORK | SIGCHLD;
而在clone中,clone_flag由用户给出。
下面给出一个使用clone的例子。
Void * func(int arg)
{
. . . . . .
}
int main()
{
int clone_flag, arg;
. . . . . .
clone_flag = CLONE_VM | CLONE_SIGHAND | CLONE_FS |
CLONE_FILES;
stack = (char *)malloc(STACK_FRAME);
stack += STACK_FRAME;
retval = clone((void *)func, stack, clone_flag, arg);
. . . . . .
}
看起来clone的用法和pthread_create有些相似,两者的最根本的差别在于clone是创建一个LWP,对核心是可见的,由核心调度,而pthread_create通常只是创建一个用户线程,对核心是不可见的,由线程库调度。
Nanosleep & sleep
sleep和nanosleep都是使进程睡眠一段时间后被唤醒,但是二者的实现完全不同。
Linux中并没有提供系统调用sleep,sleep是在库函数中实现的,它是通过调用alarm来设定报警时间,调用sigsuspend将进程挂起在信号SIGALARM上,sleep只能精确到秒级上。
nanosleep则是Linux中的系统调用,它是使用定时器来实现的,该调用使调用进程睡眠,并往定时器队列上加入一个time_list型定时器,time_list结构里包括唤醒时间以及唤醒后执行的函数,通过nanosleep加入的定时器的执行函数仅仅完成唤醒当前进程的功能。系统通过一定的机制定时检查这些队列(比如通过系统调用陷入核心后,从核心返回用户态前,要检查当前进程的时间片是否已经耗尽,如果是则调用schedule()函数重新调度,该函数中就会检查定时器队列,另外慢中断返回前也会做此检查),如果定时时间已超过,则执行定时器指定的函数唤醒调用进程。当然,由于系统时间片可能丢失,所以nanosleep精度也不是很高。
alarm也是通过定时器实现的,但是其精度只精确到秒级,另外,它设置的定时器执行函数是在指定时间向当前进程发送SIGALRM信号。
2.存储相关的系统调用
在讲述文件映射的概念时,不可避免的要牵涉到虚存(SVR 4的VM)。实际上,文件映射是虚存的中心概念,文件映射一方面给用户提供了一组措施,似的用户将文件映射到自己地址空间的某个部分,使用简单的内存访问指令读写文件;另一方面,它也可以用于内核的基本组织模式,在这种模式种,内核将整个地址空间视为诸如文件之类的一组不同对象的映射。
Unix中的传统文件访问方式是,首先用open系统调用打开文件,然后使用read,write以及lseek等调用进行顺序或者随即的I/O。这种方式是非常低效的,每一次I/O操作都需要一次系统调用。另外,如果若干个进程访问同一个文件,每个进程都要在自己的地址空间维护一个副本,浪费了内存空间。而如果能够通过一定的机制将页面映射到进程的地址空间中,也就是说首先通过简单的产生某些内存管理数据结构完成映射的创建。当进程访问页面时产生一个缺页中断,内核将页面读入内存并且更新页表指向该页面。而且这种方式非常方便于同一副本的共享。
下面给出以上两种方式的对比图:
VM是面向对象的方法设计的,这里的对象是指内存对象:内存对象是一个软件抽象的概念,它描述内存区与后备存储之间的映射。系统可以使用多种类型的后备存储,比如交换空间,本地或者远程文件以及帧缓存等等。VM系统对它们统一处理,采用同一操作集操作,比如读取页面或者回写页面等。每种不同的后备存储都可以用不同的方法实现这些操作。这样,系统定义了一套统一的接口,每种后备存储给出自己的实现方法。
这样,进程的地址空间就被视为一组映射到不同数据对象上的的映射组成。所有的有效地址就是那些映射到数据对象上的地址。这些对象为映射它的页面提供了持久性的后备存储。映射使得用户可以直接寻址这些对象。
值得提出的是,VM体系结构独立于Unix系统,所有的Unix系统语义,如正文,数据及堆栈区都可以建构在基本VM系统之上。同时,VM体系结构也是独立于存储管理的,存储管理是由操作系统实施的,如:究竟采取什么样的对换和请求调页算法,究竟是采取分段还是分页机制进行存储管理,究竟是如何将虚拟地址转换成为物理地址等等(Linux中是一种叫Three Level Page Table的机制),这些都与内存对象的概念无关。
下面介绍Linux中VM的实现。
如下图所示,一个进程应该包括一个mm_struct(memory manage struct),该结构是进程虚拟地址空间的抽象描述,里面包括了进程虚拟空间的一些管理信息:start_code, end_code, start_data, end_data, start_brk, end_brk等等信息。另外,也有一个指向进程虚存区表(vm_area_struct :virtual memory area)的指针,该链是按照虚拟地址的增长顺序排列的。
在Linux进程的地址空间被分作许多区(vma),每个区(vma)都对应虚拟地址空间上一段连续的区域,vma是可以被共享和保护的独立实体,这里的vma就是前面提到的内存对象。这里给出vm_area_struct的结构,其中,前半部分是公共的,与类型无关的一些数据成员,如:指向mm_struct的指针,地址范围等等,后半部分则是与类型相关的成员,其中最重要的是一个指向vm_operation_struct向量表的指针vm_ops,vm_pos向量表是一组虚函数,定义了与vma类型无关的接口。每一个特定的子类,即每种vma类型都必须在向量表中实现这些操作。这里包括了:open, close, unmap, protect, sync, nopage, wppage, swapout这些操作。
struct vm_area_struct {
/*公共的,与vma类型无关的 */
unsigned long vm_start;
unsigned long vm_end;
struct vm_area_struct *vm_next;
pgprot_t vm_page_prot;
unsigned long vm_flags;
short vm_avl_height;
struct vm_area_struct * vm_avl_left;
struct vm_area_struct * vm_avl_right;
struct vm_area_struct *vm_next_share;
struct vm_area_struct **vm_pprev_share;
/* 与类型相关的 */
struct vm_operations_struct * vm_ops;
unsigned long vm_pgoff;
struct file * vm_file;
unsigned long vm_raend;
void * vm_private_data;
vm_ops: open, close, no_page, swapin, swapout . . . . . .
Mmap系统调用的实现过程是:
1.先通过文件系统定位要映射的文件;
2.权限检查,映射的权限不会超过文件打开的方式,也就是说如果文件是以只读方式打开,那么则不允许建立一个可写映射;
3.创建一个vma对象,并对之进行初始化;
4.调用映射文件的mmap函数,其主要工作是给vm_ops向量表赋值;
5.把该vma链入该进程的vma链表中,如果可以和前后的vma合并则合并;
6.如果是要求VM_LOCKED(映射区不被换出)方式映射,则发出缺页请求,把映射页面读入内存中;
该调用可以看作是mmap的一个逆过程。它将进程中从start开始length长度的一段区域的映射关闭,如果该区域不是恰好对应一个vma,则有可能会分割几个或几个vma。
Msync(void * start, size_t length, int flags) :
把映射区域的修改回写到后备存储中。因为munmap时并不保证页面回写,如果不调用msync,那么有可能在munmap后丢失对映射区的修改。其中flags可以是MS_SYNC, MS_ASYNC, MS_INVALIDATE,MS_SYNC要求回写完成后才返回,MS_ASYNC发出回写请求后立即返回,MS_INVALIDATE使用回写的内容更新该文件的其它映射。
该系统调用是通过调用映射文件的sync函数来完成工作的。
brk(void * end_data_segement):
将进程的数据段扩展到end_data_segement指定的地址,该系统调用和mmap的实现方式十分相似,同样是产生一个vma,然后指定其属性。不过在此之前需要做一些合法性检查,比如该地址是否大于mm->end_code,end_data_segement和mm->brk之间是否还存在其它vma等等。通过brk产生的vma映射的文件为空,这和匿名映射产生的vma相似,关于匿名映射不做进一步介绍。我们使用的库函数malloc就是通过brk实现的,通过下面这个例子很容易证实这点:
main()
{
char * m, * n;
int size;
m = (char *)sbrk(0);
printf("sbrk addr = %08lx\n", m);
do {
n = malloc(1024);
printf("malloc addr = %08lx\n", n);
m = (char *)sbrk(0);
}
malloc addr = 08049be0
malloc addr = 08049fe8
malloc addr = 0804a3f0
new sbrk addr = 0804b000
3.进程间通信(IPC)
System V IPC包括三种机制:message(允许进程发送格式化的数据流到任意的进程)、shared memory(允许进程间共享它们虚拟地址空间的部分区域)和semaphore(允许进程间同步的执行)。
操作系统核心中为它们分别维护着一个表,这三个表是系统中所有这三种IPC对象的集合,表的索引是一个数值ID,进程通过这个ID可以查找到需要使用的IPC资源。进程每创建一个IPC对象,系统中都会在相应的表中增加一项。之后其它进程(具有许可权的进程)只要通过该IPC对象的ID则可以引用它。
IPC对象必须使用IPC_RMID命令来显示的释放,否则这个对象就处于活动状态,甚至所有的使用它的进程都已经终止。这种机制某些时候十分有用,但是也正因为这种特征,使得操作系统内核无法判断IPC对象是被用户故意遗留下来供将来其它进程使用还是被无意抛弃的。
Linux中只提供了一个系统调用接口ipc()来完成所有System V IPC操作,我们常使用的是建立在该调用之上的库函数接口。对于这三种IPC,都有很相似的三种调用:xxxget, (msgsnd, msgrcv)|semopt | (shmat, shmdt), xxxctl
Xxxget:获取调用,在系统中申请或者查询一个IPC资源,返回值是该IPC对象的ID,该调用类似于文件系统的open, create调用;
Xxxctl:控制调用,至少包括三种操作:XXX_RMID(释放IPC对象), XXX_STAT(查询状态), XXX_SET(设置状态信息);
(msgsnd, msgrcv) | Semopt | (shmat, shmdt)|:操作调用,这些调用的功能随IPC对象的类型不同而有较大差异。
4.文件系统相关的调用
文件是用来保存数据的,而文件系统则可以让用户组织,操纵以及存取不同的文件。内核允许用户通过一个严格定义的过程性接口与文件系统进行交互,这个接口对用户屏蔽了文件系统的细节,同时指定了所有相关系统调用的行为和语义。Linux支持许多中文件系统,如ext2,msdos, ntfs, proc, dev, ufs, nfs等等,这些文件系统都实现了相同的接口,因此给应用程序提供了一致性的视图。但每种文件系统在实现时可能对某个方面加以了一定的限制。如:文件名的长度,是否支持所有的文件系统接口调用。
为了支持多文件系统,sun提出了一种vnode/vfs接口,SVR4中将之实现成了一种工业标准。而Linux作为一种Unix的clone体,自然也实现了这种接口,只是它的接口定义和SVR4的稍有不同。Vnode/Vfs接口的设计体现了面向对象的思想,Vfs(虚拟文件系统)代表内核中的一个文件系统,Vnode(虚拟节点)代表内核中的一个文件,它们都可以被视为抽象基类,并可以从中派生出不同的子类以实现不同的文件系统。
由于篇幅原因,这里只是大概的介绍一下怎样通过Vnode/Vfs结构来实现文件系统和访问文件。
在Linux中支持的每种文件系统必须有一个file_system_type结构,此结构的核心是read_super函数,该函数将读取文件系统的超级块。Linux中支持的所有文件系统都会被注册在一条file_system_type结构链中,注册是在系统初始化时调用regsiter_filesystem()完成,如果文件系统是以模块的方式实现,则是在调用init_module时完成。
当mount某种块设备时,将调用系统调用mount,该调用中将会首先检查该类文件系统是否注册在系统种中,如果注册了则先给该文件系统分配一个super_block,并进行初始化,最后调用这种文件系统的read_super函数来完成super_block结构私有数据的赋值。其中最主要的工作是给super_block的s_ops赋值,s_ops是一个函数向量表,由文件系统各自实现了一组操作。
struct super_operations {
void (*read_inode) (struct inode *);
void (*write_inode) (struct inode *);
void (*put_inode) (struct inode *);
void (*delete_inode) (struct inode *);
void (*put_super) (struct super_block *);
void (*write_super) (struct super_block *);
int (*statfs) (struct super_block *, struct statfs *);
int (*remount_fs) (struct super_block *, int *, char *);
void (*clear_inode) (struct inode *);
void (*umount_begin) (struct super_block *);
};
由于这组操作中定义了文件系统中对于inode的操作,所以是之后对于文件系统中文件所有操作的基础。
在给super_block的s_ops赋值后,再给该文件系统分配一个vfsmount结构,将该结构注册到系统维护的另一条链vfsmntlist中,所有mount上的文件系统都在该链中有一项。在umount时,则从链中删除这一项并且释放超级块。
对于一个已经mount的文件系统中任何文件的操作首先应该以产生一个inode实例,即根据文件系统的类型生成一个属于该文件系统的内存i节点。这首先调用文件定位函数lookup_dentry查找目录缓存看是否使用过该文件,如果还没有则缓存中找不到,于是需要的i接点则依次调用路径上的所有目录I接点的lookup函数,在lookup函数中会调用iget函数,该函数中最终调用超级块的s_ops->read_inode读取目标文件的磁盘I节点(这一步再往下就是由设备驱动完成了,通过调用驱动程序的read函数读取磁盘I节点),read_inode函数的主要功能是初始化inode的一些私有数据(比如数据存储位置,文件大小等等)以及给inode_operations函数开关表赋值,最终该inode被绑定在一个目录缓存结构dentry中返回。
在获得了文件的inode之后,对于该文件的其它一切操作都有了根基。因为可以从inode 获得文件操作函数开关表file_operatoins,该开关表里给出了标准的文件I/O接口的实现,包括read, write, lseek, mmap, ioctl等等。这些函数入口将是所有关于文件的系统调用请求的最终处理入口,通过这些函数入口会向存储该文件的硬设备驱动发出请求并且由驱动程序返回数据。当然这中间还会牵涉到一些关于buffer的管理问题,这里就不赘述了。
通过讲述这些,我们应该明白了为什么可以使用统一的系统调用接口来访问不同文件系统类型的文件了:因为在文件系统的实现一层,都把低层的差异屏蔽了,用户可见的只是高层可见的一致的系统调用接口。
5.与module相关的系统调用
Linux中提供了往系统中添加和卸载模块的接口,create_module(),init_module (), delete_module(),这些系统调用通常不是直接为程序员使用的,它们仅仅是为实现一些系统命令而提供的接口,如insmod, rmmod,(在使用这些系统调用前必须先加载目标文件到用户进程的地址空间,这必须由目标文件格式所特定的库函数(如:libobj.a中的一些函数)来完成)。
Linux的核心中维护了一个module_list列表,每个被加载到核心中的模块都在其中占有一项,系统调用create_module()就是在该列表里注册某个指定的模块,而init_module则是使用模块目标文件内容的映射来初始化核心中注册的该模块,并且调用该模块的初始化函数,初始化函数通常完成一些特定的初始化操作,比如文件系统的初始化函数就是在操作系统中注册该文件系统。delete_module则是从系统中卸载一个模块,其主要工作是从module_list中删除该模块对应的module结构并且调用该模块的cleanup函数卸载其它私有信息。
检查系统上其它资源是否符合新内核的要求。在linux/Document目录下有一个叫Changes的文件,里面列举了当前内核版本所需要的其它软件的版本号,
- Kernel modutils 2.1.121 ; insmod -V
- Gnu C 2.7.2.3 ; gcc --version
- Binutils 2.8.1.0.23 ; ld -v
- Linux libc5 C Library 5.4.46 ; ls -l /lib/libc*
- Linux libc6 C Library 2.0.7pre6 ; ls -l /lib/libc*
- Dynamic Linker (ld.so) 1.9.9 ; ldd --version or ldd -v
- Linux C++ Library 2.7.2.8 ; ls -l /usr/lib/libg++.so.*
. . . . . .
其中最后一项是列举该软件版本号的命令,如果不符合要求先给相应软件升级,这一步通常可以忽略。
2.配置内核
使用make config或者make menuconfig, make xconfig配置新内核。其中包括选择块设备驱动程序、网络选项、网络设备支持、文件系统等等,用户可以根据自己的需求来进行功能配置。每个选项至少有“y”和“n”两种选择,选择“y”表示把相应的支持编译进内核,选“n”表示不提供这种支持,还有的有第三种选择“m”,则表示把该支持编译成可加载模块,即前面提到的module,怎样编译和安装模块在后面会介绍。
这里,顺便讲述一下如何在内核中增加自己的功能支持。
假如我们现在需要在自己的内核中加入一个文件系统tfile,在完成了文件系统的代码后,在linux/fs下建立一个tfile目录,把源文件拷贝到该目录下,然后修改linux/fs下的Makefile,把对应该文件系统的目标文件加入目标文件列表中,最后修改linux/fs/Config.in文件,加入
bool 'tfile fs support' CONFIG_TFILE_FS或者
tristate ‘tfile fs support' CONFIG_TFILE_FS
这样在Make menuconfig时在filesystem选单下就可以看到
< > tfile fs support一项了
3.编译内核
在配置好内核后就是编译内核了,在编译之前首先应该执行make dep命令建立好依赖关系,该命令将会修改linux中每个子目录下的.depend文件,该文件包含了该目录下每个目标文件所需要的头文件(绝对路径的方式列举)。
然后就是使用make bzImage命令来编译内核了。该命令运行结束后将会在linux/arch/asm/boot/产生一个名叫bzImage的映像文件。
4.使用新内核引导
把前面编译产生的映像文件拷贝到/boot目录下(也可以直接建立一个符号连接,这样可以省去每次编译后的拷贝工作),这里暂且命名为vmlinuz-new,那么再修改/etc/lilo.conf,在其中增加这么几条:
image = /boot/vmlinuz-new
root = /dev/hda1
label = new
read-only
并且运行lilo命令,那么系统在启动时就可以选用新内核引导了。
5.编译模块和使用模块
Linux下的硬件驱动——USB设备(上)(驱动配置部分)
Linux内核模块和驱动的编写步骤
Linux核心数据结构
struct blk_dev_struct {
void (*request_fn)(void);
struct request * current_request;
struct request plug;
struct tq_struct plug_tq;
};
/* bh state bits */
#define BH_Uptodate 0 /* 1 if the buffer contains valid data */
#define BH_Dirty 1 /* 1 if the buffer is dirty */
#define BH_Lock 2 /* 1 if the buffer is locked */
#define BH_Req 3 /* 0 if the buffer has been invalidated */
#define BH_Touched 4 /* 1 if the buffer has been touched (aging) */
#define BH_Has_aged 5 /* 1 if the buffer has been aged (aging) */
#define BH_Protected 6 /* 1 if the buffer is protected */
#define BH_FreeOnIO 7 /* 1 to discard the buffer_head after IO */
struct buffer_head {
/* First cache line: */
unsigned long b_blocknr; /* block number */
kdev_t b_dev; /* device (B_FREE = free) */
kdev_t b_rdev; /* Real device */
unsigned long b_rsector; /* Real buffer location on disk */
struct buffer_head *b_next; /* Hash queue list */
struct buffer_head *b_this_page; /* circular list of buffers in one
page */
/* Second cache line: */
unsigned long b_state; /* buffer state bitmap (above) */
struct buffer_head *b_next_free;
unsigned int b_count; /* users using this block */
unsigned long b_size; /* block size */
/* Non-performance-critical data follows. */
char *b_data; /* pointer to data block */
unsigned int b_list; /* List that this buffer appears */
unsigned long b_flushtime; /* Time when this (dirty) buffer
* should be written */
unsigned long b_lru_time; /* Time when this buffer was
* last used. */
struct wait_queue *b_wait;
struct buffer_head *b_prev; /* doubly linked hash list */
struct buffer_head *b_prev_free; /* doubly linked list of buffers */
struct buffer_head *b_reqnext; /* request queue */
};
struct device
{
/*
* This is the first field of the "visible" part of this structure
* (i.e. as seen by users in the "Space.c" file). It is the name
* the interface.
*/
char *name;
/* I/O specific fields */
unsigned long rmem_end; /* shmem "recv" end */
unsigned long rmem_start; /* shmem "recv" start */
unsigned long mem_end; /* shared mem end */
unsigned long mem_start; /* shared mem start */
unsigned long base_addr; /* device I/O address */
unsigned char irq; /* device IRQ number */
/* Low-level status flags. */
volatile unsigned char start, /* start an operation */
interrupt; /* interrupt arrived */
unsigned long tbusy; /* transmitter busy */
struct device *next;
/* The device initialization function. Called only once. */
int (*init)(struct device *dev);
/* Some hardware also needs these fields, but they are not part of
the usual set specified in Space.c. */
unsigned char if_port; /* Selectable AUI,TP, */
unsigned char dma; /* DMA channel */
struct enet_statistics* (*get_stats)(struct device *dev);
/*
* This marks the end of the "visible" part of the structure. All
* fields hereafter are internal to the system, and may change at
* will (read: may be cleaned up at will).
*/
/* These may be needed for future network-power-down code. */
unsigned long trans_start; /* Time (jiffies) of
last transmit */
unsigned long last_rx; /* Time of last Rx */
unsigned short flags; /* interface flags (BSD)*/
unsigned short family; /* address family ID */
unsigned short metric; /* routing metric */
unsigned short mtu; /* MTU value */
unsigned short type; /* hardware type */
unsigned short hard_header_len; /* hardware hdr len */
void *priv; /* private data */
/* Interface address info. */
unsigned char broadcast[MAX_ADDR_LEN];
unsigned char pad;
unsigned char dev_addr[MAX_ADDR_LEN];
unsigned char addr_len; /* hardware addr len */
unsigned long pa_addr; /* protocol address */
unsigned long pa_brdaddr; /* protocol broadcast addr*/
unsigned long pa_dstaddr; /* protocol P-P other addr*/
unsigned long pa_mask; /* protocol netmask */
unsigned short pa_alen; /* protocol address len */
struct dev_mc_list *mc_list; /* M'cast mac addrs */
int mc_count; /* No installed mcasts */
struct ip_mc_list *ip_mc_list; /* IP m'cast filter chain */
__u32 tx_queue_len; /* Max frames per queue */
/* For load balancing driver pair support */
unsigned long pkt_queue; /* Packets queued */
struct device *slave; /* Slave device */
struct net_alias_info *alias_info; /* main dev alias info */
struct net_alias *my_alias; /* alias devs */
/* Pointer to the interface buffers. */
struct sk_buff_head buffs[DEV_NUMBUFFS];
/* Pointers to interface service routines. */
int (*open)(struct device *dev);
int (*stop)(struct device *dev);
int (*hard_start_xmit) (struct sk_buff *skb,
struct device *dev);
int (*hard_header) (struct sk_buff *skb,
struct device *dev,
unsigned short type,
void *daddr,
void *saddr,
unsigned len);
int (*rebuild_header)(void *eth,
struct device *dev,
unsigned long raddr,
struct sk_buff *skb);
void (*set_multicast_list)(struct device *dev);
int (*set_mac_address)(struct device *dev,
void *addr);
int (*do_ioctl)(struct device *dev,
struct ifreq *ifr,
int cmd);
int (*set_config)(struct device *dev,
struct ifmap *map);
void (*header_cache_bind)(struct hh_cache **hhp,
struct device *dev,
unsigned short htype,
__u32 daddr);
void (*header_cache_update)(struct hh_cache *hh,
struct device *dev,
unsigned char * haddr);
int (*change_mtu)(struct device *dev,
int new_mtu);
struct iw_statistics* (*get_wireless_stats)(struct device *dev);
};
struct device_struct {
const char * name;
struct file_operations * fops;
};
struct file {
mode_t f_mode;
loff_t f_pos;
unsigned short f_flags;
unsigned short f_count;
unsigned long f_reada, f_ramax, f_raend, f_ralen, f_rawin;
struct file *f_next, *f_prev;
int f_owner; /* pid or -pgrp where SIGIO should be sent */
struct inode * f_inode;
struct file_operations * f_op;
unsigned long f_version;
void *private_data; /* needed for tty driver, and maybe others */
};
struct files_struct {
int count;
fd_set close_on_exec;
fd_set open_fds;
struct file * fd[NR_OPEN];
};
struct fs_struct {
int count;
unsigned short umask;
struct inode * root, * pwd;
};
struct hd_struct {
long start_sect;
long nr_sects;
};
struct gendisk {
int major; /* major number of driver */
const char *major_name; /* name of major driver */
int minor_shift; /* number of times minor is shifted to
get real minor */
int max_p; /* maximum partitions per device */
int max_nr; /* maximum number of real devices */
void (*init)(struct gendisk *);
/* Initialization called before we
do our thing */
struct hd_struct *part; /* partition table */
int *sizes; /* device size in blocks, copied to
blk_size[] */
int nr_real; /* number of real devices */
void *real_devices; /* internal use */
struct gendisk *next;
};
struct inode {
kdev_t i_dev;
unsigned long i_ino;
umode_t i_mode;
nlink_t i_nlink;
uid_t i_uid;
gid_t i_gid;
kdev_t i_rdev;
off_t i_size;
time_t i_atime;
time_t i_mtime;
time_t i_ctime;
unsigned long i_blksize;
unsigned long i_blocks;
unsigned long i_version;
unsigned long i_nrpages;
struct semaphore i_sem;
struct inode_operations *i_op;
struct super_block *i_sb;
struct wait_queue *i_wait;
struct file_lock *i_flock;
struct vm_area_struct *i_mmap;
struct page *i_pages;
struct dquot *i_dquot[MAXQUOTAS];
struct inode *i_next, *i_prev;
struct inode *i_hash_next, *i_hash_prev;
struct inode *i_bound_to, *i_bound_by;
struct inode *i_mount;
unsigned short i_count;
unsigned short i_flags;
unsigned char i_lock;
unsigned char i_dirt;
unsigned char i_pipe;
unsigned char i_sock;
unsigned char i_seek;
unsigned char i_update;
unsigned short i_writecount;
union {
struct pipe_inode_info pipe_i;
struct minix_inode_info minix_i;
struct ext_inode_info ext_i;
struct ext2_inode_info ext2_i;
struct hpfs_inode_info hpfs_i;
struct msdos_inode_info msdos_i;
struct umsdos_inode_info umsdos_i;
struct iso_inode_info isofs_i;
struct nfs_inode_info nfs_i;
struct xiafs_inode_info xiafs_i;
struct sysv_inode_info sysv_i;
struct affs_inode_info affs_i;
struct ufs_inode_info ufs_i;
struct socket socket_i;
void *generic_ip;
} u;
};
struct ipc_perm
{
key_t key;
ushort uid; /* owner euid and egid */
ushort gid;
ushort cuid; /* creator euid and egid */
ushort cgid;
ushort mode; /* access modes see mode flags below */
ushort seq; /* sequence number */
};
struct irqaction {
void (*handler)(int, void *, struct pt_regs *);
unsigned long flags;
unsigned long mask;
const char *name;
void *dev_id;
struct irqaction *next;
};
struct linux_binfmt {
struct linux_binfmt * next;
long *use_count;
int (*load_binary)(struct linux_binprm *, struct pt_regs * regs);
int (*load_shlib)(int fd);
int (*core_dump)(long signr, struct pt_regs * regs);
};
typedef struct page {
/* these must be first (free area handling) */
struct page *next;
struct page *prev;
struct inode *inode;
unsigned long offset;
struct page *next_hash;
atomic_t count;
unsigned flags; /* atomic flags, some possibly
updated asynchronously */
unsigned dirty:16,
age:8;
struct wait_queue *wait;
struct page *prev_hash;
struct buffer_head *buffers;
unsigned long swap_unlock_entry;
unsigned long map_nr; /* page->map_nr == page - mem_map */
} mem_map_t;
struct mm_struct {
int count;
pgd_t * pgd;
unsigned long context;
unsigned long start_code, end_code, start_data, end_data;
unsigned long start_brk, brk, start_stack, start_mmap;
unsigned long arg_start, arg_end, env_start, env_end;
unsigned long rss, total_vm, locked_vm;
unsigned long def_flags;
struct vm_area_struct * mmap;
struct vm_area_struct * mmap_avl;
struct semaphore mmap_sem;
};
struct pci_bus {
struct pci_bus *parent; /* parent bus this bridge is on */
struct pci_bus *children; /* chain of P2P bridges on this bus */
struct pci_bus *next; /* chain of all PCI buses */
struct pci_dev *self; /* bridge device as seen by parent */
struct pci_dev *devices; /* devices behind this bridge */
void *sysdata; /* hook for sys-specific extension */
unsigned char number; /* bus number */
unsigned char primary; /* number of primary bridge */
unsigned char secondary; /* number of secondary bridge */
unsigned char subordinate; /* max number of subordinate buses */
};
/*
* There is one pci_dev structure for each slot-number/function-number
* combination:
*/
struct pci_dev {
struct pci_bus *bus; /* bus this device is on */
struct pci_dev *sibling; /* next device on this bus */
struct pci_dev *next; /* chain of all devices */
void *sysdata; /* hook for sys-specific extension */
unsigned int devfn; /* encoded device & function index */
unsigned short vendor;
unsigned short device;
unsigned int class; /* 3 bytes: (base,sub,prog-if) */
unsigned int master : 1; /* set if device is master capable */
/*
* In theory, the irq level can be read from configuration
* space and all would be fine. However, old PCI chips don't
* support these registers and return 0 instead. For example,
* the Vision864-P rev 0 chip can uses INTA, but returns 0 in
* the interrupt line and pin registers. pci_init()
* initializes this field with the value at PCI_INTERRUPT_LINE
* and it is the job of pcibios_fixup() to change it if
* necessary. The field must not be 0 unless the device
* cannot generate interrupts at all.
*/
unsigned char irq; /* irq generated by this device */
};
struct request {
volatile int rq_status;
#define RQ_INACTIVE (-1)
#define RQ_ACTIVE 1
#define RQ_SCSI_BUSY 0xffff
#define RQ_SCSI_DONE 0xfffe
#define RQ_SCSI_DISCONNECTING 0xffe0
kdev_t rq_dev;
int cmd; /* READ or WRITE */
int errors;
unsigned long sector;
unsigned long nr_sectors;
unsigned long current_nr_sectors;
char * buffer;
struct semaphore * sem;
struct buffer_head * bh;
struct buffer_head * bhtail;
struct request * next;
};
struct rtable
{
struct rtable *rt_next;
__u32 rt_dst;
__u32 rt_src;
__u32 rt_gateway;
atomic_t rt_refcnt;
atomic_t rt_use;
unsigned long rt_window;
atomic_t rt_lastuse;
struct hh_cache *rt_hh;
struct device *rt_dev;
unsigned short rt_flags;
unsigned short rt_mtu;
unsigned short rt_irtt;
unsigned char rt_tos;
};
struct semaphore {
int count;
int waking;
int lock ; /* to make waking testing atomic */
struct wait_queue *wait;
};
struct sk_buff
{
struct sk_buff *next; /* Next buffer in list */
struct sk_buff *prev; /* Previous buffer in list */
struct sk_buff_head *list; /* List we are on */
int magic_debug_cookie;
struct sk_buff *link3; /* Link for IP protocol level buffer chains */
struct sock *sk; /* Socket we are owned by */
unsigned long when; /* used to compute rtt's */
struct timeval stamp; /* Time we arrived */
struct device *dev; /* Device we arrived on/are leaving by */
union
{
struct tcphdr *th;
struct ethhdr *eth;
struct iphdr *iph;
struct udphdr *uh;
unsigned char *raw;
/* for passing file handles in a unix domain socket */
void *filp;
} h;
union
{
/* As yet incomplete physical layer views */
unsigned char *raw;
struct ethhdr *ethernet;
} mac;
struct iphdr *ip_hdr; /* For IPPROTO_RAW */
unsigned long len; /* Length of actual data */
unsigned long csum; /* Checksum */
__u32 saddr; /* IP source address */
__u32 daddr; /* IP target address */
__u32 raddr; /* IP next hop address */
__u32 seq; /* TCP sequence number */
__u32 end_seq; /* seq [+ fin] [+ syn] + datalen */
__u32 ack_seq; /* TCP ack sequence number */
unsigned char proto_priv[16];
volatile char acked, /* Are we acked ? */
used, /* Are we in use ? */
free, /* How to free this buffer */
arp; /* Has IP/ARP resolution finished */
unsigned char tries, /* Times tried */
lock, /* Are we locked ? */
localroute, /* Local routing asserted for this frame */
pkt_type, /* Packet class */
pkt_bridged, /* Tracker for bridging */
ip_summed; /* Driver fed us an IP checksum */
#define PACKET_HOST 0 /* To us */
#define PACKET_BROADCAST 1 /* To all */
#define PACKET_MULTICAST 2 /* To group */
#define PACKET_OTHERHOST 3 /* To someone else */
unsigned short users; /* User count - see datagram.c,tcp.c */
unsigned short protocol; /* Packet protocol from driver. */
unsigned int truesize; /* Buffer size */
atomic_t count; /* reference count */
struct sk_buff *data_skb; /* Link to the actual data skb */
unsigned char *head; /* Head of buffer */
unsigned char *data; /* Data head pointer */
unsigned char *tail; /* Tail pointer */
unsigned char *end; /* End pointer */
void (*destructor)(struct sk_buff *); /* Destruct function */
__u16 redirport; /* Redirect port */
};
struct sock
{
/* This must be first. */
struct sock *sklist_next;
struct sock *sklist_prev;
struct options *opt;
atomic_t wmem_alloc;
atomic_t rmem_alloc;
unsigned long allocation; /* Allocation mode */
__u32 write_seq;
__u32 sent_seq;
__u32 acked_seq;
__u32 copied_seq;
__u32 rcv_ack_seq;
unsigned short rcv_ack_cnt; /* count of same ack */
__u32 window_seq;
__u32 fin_seq;
__u32 urg_seq;
__u32 urg_data;
__u32 syn_seq;
int users; /* user count */
/*
* Not all are volatile, but some are, so we
* might as well say they all are.
*/
volatile char dead,
urginline,
intr,
blog,
done,
reuse,
keepopen,
linger,
delay_acks,
destroy,
ack_timed,
no_check,
zapped,
broadcast,
nonagle,
bsdism;
unsigned long lingertime;
int proc;
struct sock *next;
struct sock **pprev;
struct sock *bind_next;
struct sock **bind_pprev;
struct sock *pair;
int hashent;
struct sock *prev;
struct sk_buff *volatile send_head;
struct sk_buff *volatile send_next;
struct sk_buff *volatile send_tail;
struct sk_buff_head back_log;
struct sk_buff *partial;
struct timer_list partial_timer;
long retransmits;
struct sk_buff_head write_queue,
receive_queue;
struct proto *prot;
struct wait_queue **sleep;
__u32 daddr;
__u32 saddr; /* Sending source */
__u32 rcv_saddr; /* Bound address */
unsigned short max_unacked;
unsigned short window;
__u32 lastwin_seq; /* sequence number when we last
updated the window we offer */
__u32 high_seq; /* sequence number when we did
current fast retransmit */
volatile unsigned long ato; /* ack timeout */
volatile unsigned long lrcvtime; /* jiffies at last data rcv */
volatile unsigned long idletime; /* jiffies at last rcv */
unsigned int bytes_rcv;
/*
* mss is min(mtu, max_window)
*/
unsigned short mtu; /* mss negotiated in the syn's */
volatile unsigned short mss; /* current eff. mss - can change */
volatile unsigned short user_mss; /* mss requested by user in ioctl */
volatile unsigned short max_window;
unsigned long window_clamp;
unsigned int ssthresh;
unsigned short num;
volatile unsigned short cong_window;
volatile unsigned short cong_count;
volatile unsigned short packets_out;
volatile unsigned short shutdown;
volatile unsigned long rtt;
volatile unsigned long mdev;
volatile unsigned long rto;
volatile unsigned short backoff;
int err, err_soft; /* Soft holds errors that don't
cause failure but are the cause
of a persistent failure not
just 'timed out' */
unsigned char protocol;
volatile unsigned char state;
unsigned char ack_backlog;
unsigned char max_ack_backlog;
unsigned char priority;
unsigned char debug;
int rcvbuf;
int sndbuf;
unsigned short type;
unsigned char localroute; /* Route locally only */
/*
* This is where all the private (optional) areas that don't
* overlap will eventually live.
*/
union
{
struct unix_opt af_unix;
#if defined(CONFIG_ATALK) || defined(CONFIG_ATALK_MODULE)
struct atalk_sock af_at;
#endif
#if defined(CONFIG_IPX) || defined(CONFIG_IPX_MODULE)
struct ipx_opt af_ipx;
#endif
#ifdef CONFIG_INET
struct inet_packet_opt af_packet;
#ifdef CONFIG_NUTCP
struct tcp_opt af_tcp;
#endif
#endif
} protinfo;
/*
* IP 'private area'
*/
int ip_ttl; /* TTL setting */
int ip_tos; /* TOS */
struct tcphdr dummy_th;
struct timer_list keepalive_timer; /* TCP keepalive hack */
struct timer_list retransmit_timer; /* TCP retransmit timer */
struct timer_list delack_timer; /* TCP delayed ack timer */
int ip_xmit_timeout; /* Why the timeout is running */
struct rtable *ip_route_cache; /* Cached output route */
unsigned char ip_hdrincl; /* Include headers ? */
#ifdef CONFIG_IP_MULTICAST
int ip_mc_ttl; /* Multicasting TTL */
int ip_mc_loop; /* Loopback */
char ip_mc_name[MAX_ADDR_LEN]; /* Multicast device name */
struct ip_mc_socklist *ip_mc_list; /* Group array */
#endif
/*
* This part is used for the timeout functions (timer.c).
*/
int timeout; /* What are we waiting for? */
struct timer_list timer; /* This is the TIME_WAIT/receive
* timer when we are doing IP
*/
struct timeval stamp;
/*
* Identd
*/
struct socket *socket;
/*
* Callbacks
*/
void (*state_change)(struct sock *sk);
void (*data_ready)(struct sock *sk,int bytes);
void (*write_space)(struct sock *sk);
void (*error_report)(struct sock *sk);
};
struct socket {
short type; /* SOCK_STREAM, ... */
socket_state state;
long flags;
struct proto_ops *ops; /* protocols do most everything */
void *data; /* protocol data */
struct socket *conn; /* server socket connected to */
struct socket *iconn; /* incomplete client conn.s */
struct socket *next;
struct wait_queue **wait; /* ptr to place to wait on */
struct inode *inode;
struct fasync_struct *fasync_list; /* Asynchronous wake up list */
struct file *file; /* File back pointer for gc */
};
struct task_struct {
/* these are hardcoded - don't touch */
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
long counter;
long priority;
unsigned long signal;
unsigned long blocked; /* bitmap of masked signals */
unsigned long flags; /* per process flags, defined below */
int errno;
long debugreg[8]; /* Hardware debugging registers */
struct exec_domain *exec_domain;
/* various fields */
struct linux_binfmt *binfmt;
struct task_struct *next_task, *prev_task;
struct task_struct *next_run, *prev_run;
unsigned long saved_kernel_stack;
unsigned long kernel_stack_page;
int exit_code, exit_signal;
/* ??? */
unsigned long personality;
int dumpable:1;
int did_exec:1;
int pid;
int pgrp;
int tty_old_pgrp;
int session;
/* boolean value for session group leader */
int leader;
int groups[NGROUPS];
/*
* pointers to (original) parent process, youngest child, younger sibling,
* older sibling, respectively. (p->father can be replaced with
* p->p_pptr->pid)
*/
struct task_struct *p_opptr, *p_pptr, *p_cptr,
*p_ysptr, *p_osptr;
struct wait_queue *wait_chldexit;
unsigned short uid,euid,suid,fsuid;
unsigned short gid,egid,sgid,fsgid;
unsigned long timeout, policy, rt_priority;
unsigned long it_real_value, it_prof_value, it_virt_value;
unsigned long it_real_incr, it_prof_incr, it_virt_incr;
struct timer_list real_timer;
long utime, stime, cutime, cstime, start_time;
/* mm fault and swap info: this can arguably be seen as either
mm-specific or thread-specific */
unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
int swappable:1;
unsigned long swap_address;
unsigned long old_maj_flt; /* old value of maj_flt */
unsigned long dec_flt; /* page fault count of the last time */
unsigned long swap_cnt; /* number of pages to swap on next pass */
/* limits */
struct rlimit rlim[RLIM_NLIMITS];
unsigned short used_math;
char comm[16];
/* file system info */
int link_count;
struct tty_struct *tty; /* NULL if no tty */
/* ipc stuff */
struct sem_undo *semundo;
struct sem_queue *semsleeping;
/* ldt for this task - used by Wine. If NULL, default_ldt is used */
struct desc_struct *ldt;
/* tss for this task */
struct thread_struct tss;
/* filesystem information */
struct fs_struct *fs;
/* open file information */
struct files_struct *files;
/* memory management info */
struct mm_struct *mm;
/* signal handlers */
struct signal_struct *sig;
#ifdef __SMP__
int processor;
int last_processor;
int lock_depth; /* Lock depth.
We can context switch in and out
of holding a syscall kernel lock... */
#endif
};
struct timer_list {
struct timer_list *next;
struct timer_list *prev;
unsigned long expires;
unsigned long data;
void (*function)(unsigned long);
};
struct tq_struct {
struct tq_struct *next; /* linked list of active bh's */
int sync; /* must be initialized to zero */
void (*routine)(void *); /* function to call */
void *data; /* argument to function */
};
struct vm_area_struct {
struct mm_struct * vm_mm; /* VM area parameters */
unsigned long vm_start;
unsigned long vm_end;
pgprot_t vm_page_prot;
unsigned short vm_flags;
/* AVL tree of VM areas per task, sorted by address */
short vm_avl_height;
struct vm_area_struct * vm_avl_left;
struct vm_area_struct * vm_avl_right;
/* linked list of VM areas per task, sorted by address */
struct vm_area_struct * vm_next;
/* for areas with inode, the circular list inode->i_mmap */
/* for shm areas, the circular list of attaches */
/* otherwise unused */
struct vm_area_struct * vm_next_share;
struct vm_area_struct * vm_prev_share;
/* more */
struct vm_operations_struct * vm_ops;
unsigned long vm_offset;
struct inode * vm_inode;
unsigned long vm_pte; /* shared mem */
Linux系统可卸载内核模块完全指南(下)
Linux系统可卸载内核模块完全指南(中)
Linux系统可卸载内核模块完全指南(上)