windows下文件被占用的奇怪问题
最近写的一个HTTP转码服务,父进程采用mongoose做一个小型的http server,提供文件上传下载,并且提供restfull的api接口。接收到转码请求后,通过CreateProcess创建子进程,进行文件的转码。但是偶现的一个奇怪问题是,某些子进程转码时会报错,被转码文件正在被其他进程访问,无法打开。于是在网上找了一圈,有了上一篇文章《Windows下找出占用文件的进程》。把占用文件的进程一打印出来,发现居然是其他正在转码的兄弟进程,难道转码进程本身有bug,会扫描其他待转码的文件?
由于转码服务主要调用了一些第三方的dll,没有源码。因此想采用类似linux strace一样的命令,跟踪一下转码过程中访问过的文件。结果找到这么一个工具 https://drmemory.org/page_drstrace.html。 Dr strace还挺好用的,但是分析好多次转码的过程,都没有发现转码会访问其他待转码文件。
后来想到,会不会是转码的程序会用到某些全局变量之类的,会在多个进程间互相影响?于是写了批处理,start cmd /k "转码命令",这样的方式进行多进程的并发进行,但是仍然没有发现。
在没有头绪之际,看drstrace的文档时,看到它有Child Processes这一段,突然想到这个文件句柄会不会是父进程传给转码子进程的呢?搜索到这么一篇文章
https://www.cnblogs.com/beweirdo/p/14708884.html
看到CreateProcess函数有一个bInheritHandles这样的参数。而我调用CreateProcess时,这个参数设置成了TRUE。因此问题的原因立马就清晰了。父进程在接受转码请求的同时,可能也在接收其他待转码文件的上传。在接收上传的时候,使用fopen打开了这些文件,并用fwrite持续地写入。假如一个文件还没上传完,这个时候父进程又启动了一个子进程进行转码,这个文件句柄就会被继承到这个转码子进程,在整个这个转码子进程的生命周期,这个上传文件的句柄都会被子进程持有,即使父进程关闭文件了,该文件还是无法写入。
找到问题了,要解决是不是简单的把CreateProcess的bInheritHandles设置为FALSE就可以呢?还不行。因为我在启动子进程的时候,需要把子进程的stdout重定向到一个临时文件,用于分析它的执行结果。重定向采用的方法是
https://stackoverflow.com/questions/7018228/how-do-i-redirect-output-to-a-file-with-createprocess
把bInheritHandles设置成FALSE,子进程就无法把stdout重定向到父进程CreateFile打开的这个文件了。因此只能想办法将fopen打开的上传中的文件,句柄不继承到子进程中
https://comp.os.ms-windows.programmer.win32.narkive.com/dO2ItEaF/disabling-filehandle-inheritance-by-child-processes
https://stackoverflow.com/questions/3989545/how-do-i-get-the-file-handle-from-the-fopen-file-structure
找到的第一种办法,是通过连续调用_fileno、_get_osfhandle,从FILE *获取到对应的HANDLE。然后再通过 SetHandleInformation 将 HANDLE 设置成不可继承的
后面找到一种更简单的
https://wiki.sei.cmu.edu/confluence/display/c/WIN03-C.+Understand+HANDLE+inheritance
fopen的时候直接加上 “N” 的模式参数