免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 10397 | 回复: 12
打印 上一主题 下一主题

bug? linux 下正在运行的程序是可以被删除的 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-05-12 13:59 |只看该作者 |倒序浏览
... 无聊中测试了一下 正在运行的程序 他在硬盘上的运行文件本身 居然可以删除掉...

论坛徽章:
0
2 [报告]
发表于 2010-05-12 14:01 |只看该作者
linux ??

论坛徽章:
0
3 [报告]
发表于 2010-05-12 14:03 |只看该作者
我不明白的是windows为什么不给删。

论坛徽章:
0
4 [报告]
发表于 2010-05-12 14:32 |只看该作者
windows应该也可以删除吧,只是稍微麻烦点?

论坛徽章:
1
天秤座
日期:2014-04-27 07:42:20
5 [报告]
发表于 2010-05-12 14:40 |只看该作者
嗯,从unix到现在的各种类unix,一直可以删除的,从来就没有改变过

论坛徽章:
0
6 [报告]
发表于 2010-05-12 15:45 |只看该作者
转载自http://dev.firnow.com/course/6_s ... 0090314/161422.html

       

今天被朋友问及“Linux下可以替换运行中的程序么?”,以前依稀记得Linux下是可以的(而Windows就不让),于是随口答道“OK”。结果朋友发来一个执行结果:(test正在运行中)
# cp test2 test cp: cannot create regular file `test': Text file busy

看起来是程序被占用,无法覆盖。于是自己又再做了几个实验:

(1)先rm删除正在运行的test,然后cp test2 test就没有错误了。
(2)先mv改名正在运行的test,然后cp test2 test也没有问题。

查了查资料并动手分析了一下,找到了比较满意的解释。cp并不改变目标文件的inode,事实上它的实现是这样的:
# strace cp test2 test  2>&1 | grep open.*test open("test2", O_RDONLY|O_LARGEFILE)     = 3 open("test", O_WRONLY|O_TRUNC|O_LARGEFILE) = 4

我原以为cp的实现是“rm + open(O_CREAT)”,不过现在想想上面的实现方式才是最可靠的(保证了时序安全和目标文件的属性)。这也可以解释为什么cp的目标文件会继承被覆盖文件的属性而非源文件。

Linux由于Demand Paging机制的关系,必须确保正在运行中的程序镜像(注意,并非文件本身)不被意外修改,因此内核在启动程序后会锁定这个程序镜像的inode。这就是为什么cp在用“O_WRONLY|O_TRUNC”模式open目标文件时会失败。而先rm再cp的话,新文件的inode其实已经改变了,原 inode并没有被真正删除,直到内核释放对它的引用。同理,mv只是改变了文件名,其inode不变,新文件使用了新的inode。

问题到这里已经水落石出,不过刨根究底的个性驱使我再做了以下一组实验,没想到结果完全出乎我意料之外!

写了一个简单的测试程序:
#include <stdio.h> int main(int argc, char * argv[]) { foo();  // An export function by libtest.so. sleep(1000); return 0; }

foo()是另一个测试动态库libtest.so的导出接口,只打印一行提示就返回。接下来我把上面对执行文件的测试用例对动态库又做了一遍:

(1)cp libtest2.so libtest.so可以直接覆盖已加载的动态库。
(2)先rm删除已加载的libtest.so,然后cp libtest2.so libtest.so成功。
(3)先mv改名已加载的libtest.so,然后cp libtest2.so libtest.so成功。

除了第一个用例外,结果相同。这样看来,动态库被加载时难道ld并没有锁定inode?不过想想也可以宽恕,毕竟ld也是用户态程序,没有权利去锁定inode,也不应与内核的文件系统底层实现耦合。

到这里都还算在情理之中,看起来Linux也都处理的很好。不过还剩下一个问题:动态库被以cp的方式覆盖后难道不会和Demand Paging机制产生冲突?

在思考这个问题的过程中,我意识到前面这个测试程序的一个致命漏洞,稍作修改如下:
#include <stdio.h> int main(int argc, char * argv[]) { loop: foo();  // An export function by libtest.so. sleep(1); goto loop; return 0; }

这次,再执行上面的三个用例后发现,“cp libtest2.so libtest.so”虽然仍可直接覆盖已加载的动态库,但是测试程序马上出现了“Segmentation fault”。而后两个用例结果不变。由此可见,想要安全的替换已加载的动态库,还是用“笨拙”的“rm + cp”吧,看似捷径的“cp覆盖”会直接葬送掉你的程序……

看来,我再一次低估了Linux的健壮性,看似符合逻辑的流程也可能会带来灾难性的后果;“rm & cp”与“cp覆盖”背后所隐藏的底层差异却可以成为你的救星。Linux用得越久越是让人觉得这是一块充满了荆棘和陷阱的原始丛林,只有步步为营实踏前行才能走的更远。

注:以上实验基于SuSE Linux Enterprise Server 9 SP1(Linux 2.6.5 & glibc 2.3.3)。

论坛徽章:
0
7 [报告]
发表于 2010-05-13 10:52 |只看该作者
1)cp libtest2.so libtest.so可以直接覆盖已加载的动态库。


为什么可以cp 呢。 因为ld 没有lock 住那个inode. 而运行的话 ,系统是lock 住的

从文章中看
说法好像是

系统lock 住的话 一旦rm , 系统不会真rm

而ld 的话 系统没有lock 住这个inode 所以出问题的。

但是我感觉 如果已经加载了,应该和原文件 没有关系啊

论坛徽章:
0
8 [报告]
发表于 2010-05-13 11:11 |只看该作者
删除一个文件是否是unlink一个文件? 而unlink只是把inode和目录项之间断开联系。而如果当前文件已经被打开,它的inode是不会被释放的,只是unlink,最后当关闭的时候才释放。
不知这个解释是否合理。      @new-bie@

论坛徽章:
0
9 [报告]
发表于 2010-05-13 11:12 |只看该作者
LS的解释深入浅出,通俗易懂。

论坛徽章:
0
10 [报告]
发表于 2010-05-13 13:21 |只看该作者
而可执行的程序 正在运行中

直接cp 出错  是因为 inode 是被保护的吧

如果rm +cp  就是 首先rm 掉,但是实际inode 没释放, 所以没问题。




但已加载的动态库为什么能被cp 成功,而且还会出现运行错误呢?

1 inode 没被内核进行所谓的计数保护?? 和可执行的情况不一样。所以可以被覆盖。


这次,再执行上面的三个用例后发现,“cp libtest2.so libtest.so”虽然仍可直接覆盖已加载的动态库,但是测试程序马上出现了“Segmentation fault”。


无论是否将 inode 删除掉。 如果已经载入内存的库,会因为本地文件的修改而改变吗?

已经加载的动态库,为什么会在被覆盖动态库文件以后 crash 呢?难道他又去读了 源文件
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP