僵尸进程
僵尸进程是当子进程比父进程先结束,而父进程又没有回收子进程,释放子进程占用的资源,此时子进程将成为一个僵尸进程。如果父进程先退出 ,子进程被init接管,子进程退出后init会回收其占用的相关资源
我们都知道进程的工作原理。我们启动一个程序,开始我们的任务,然后等任务结束了,我们就停止这个进程。 进程停止后, 该进程就会从进程表中移除。你可以通过 System-Monitor 查看当前进程。 在UNIX 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他, 那么他将变成一个僵尸进程。 但是如果该进程的父进程已经先结束了,那么该进程就不会变成僵尸进程, 因为每个进程结束的时候,系统都会扫描当前系统中所运行的所有进程, 看有没有哪个进程是刚刚结束的这个进程的子进程,如果是的话,就由Init 来接管他,成为他的父进程……
这个程序就是子进程比父进程先结束,那么子进程就会就会进入僵尸状态,我们在后台运行程序,然后在子进程结束后ps查看当前进程,就可以看到pid为2741进入了僵尸进程
僵尸进程的本质就是子进程率先结束,导致了父进程未获取到子进程的退出码
那么解决僵尸进程的方式也很简单,就是让子进程结束后等待父进程结束,然后获取到子进程的退出码,然后程序正常结束退出
解决问题的关键就是让父进程获取到子进程的退出码
这里使用wait()
这里看到wait的作用就是让子进程阻塞住
这里就可以用wait阻塞子进程
这里就可以返回子进程的退出码从而避免僵尸进程的产生
孤儿进程
写时拷贝技术
简单的来说就是就像银行的主卡和副卡,副卡可当做除了和主卡的联系外是没有任何信息的,当你用钱时即要改变余额数量时 ,银行系统把主卡的信息拷贝一份到副卡。
在Linux程序中,fork()会产生一个和父进程完全相同的子进程,但子进程在此后多会exec系统调用,出于效率考虑,linux中引入了“写时复制“技术,也就是只有进程空间的各段的内容要发生变化时,才会将父进程的内容复制一份给子进程。
那么子进程的物理空间没有代码,怎么去取指令执行exec系统调用呢?
在fork之后exec之前两个进程用的是相同的物理空间(内存区),子进程的代码段、数据段、堆栈都是指向父进程的物理空间,也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个。当父子进程中有更改相应段的行为发生时,再为子进程相应的段分配物理空间,如果不是因为exec,内核会给子进程的数据段、堆栈段分配相应的物理空间(至此两者有各自的进程空间,互不影响),而代码段继续共享父进程的物理空间(两者的代码完全相同)。而如果是因为exec,由于两者执行的代码不同,子进程的代码段也会分配单独的物理空间。
在网上看到还有个细节问题就是,fork之后内核会通过将子进程放在队列的前面,以让子进程先执行,以免父进程执行导致写时复制,而后子进程执行exec系统调用,因无意义的复制而造成效率的下降。
Linux文件操作
对比windows中的文件操作
在windows中,我们使用open函数对一个文件进行打开
参数分别是 路径+文件名 打开模式 权限问题
那么在Linux中,文件的操作和Windows类似但不完全相同
我们可以查看open函数在Linux中使用的要求
这是Linux中要引入文件操作的头文件
这里可以看到,open中的参数对比window中会有不同的地方
所以我们还要关注open中的参数
其实这个和windows中的打开模式有类似的地方
那么我们就创建一个文件来看看Linux中的文件操作有什么不同于windows中的地方
根据open的使用方法,main程序运行之后就会自己创建一个a.txt文件在当前目录下
结果和我们预想的一样
我们创建了一个空的普通文件
那么我们要如何要往里面写入内容呢
这里使用write函数
这样就向文件里写入了一个hello
这里我们要关注Linux中wirte和 read的使用
这里可以看到write是没有返回值的
而read是有返回值的 返回值就是read读取到的个数
在Linux中有cp命令
那么我们可不可以通过读写完成一个自己的mycp命令
那么根据这个程序运行之后会在本路径下创建一个b.txt文件
然后将a.txt中的内容给到b.txt
我们用ls命令查看一下
这样就可以实现了自己的mycp命令
我们可以将编译好的可执行程序移动到系统的命令库中
这样就可以直接以命令的方式进行执行
文件操作和进程复制结合
这里就涉及到了关于进程PCB中的一个文件信息表中
有一个关于文件的结构体中有一个计数器
在close后会回收文件的信息
那么如果我们调换 open和 fork的位置
可以观察输出结果的改变
如果先fork再open
那么父子进程都会使用close
因此输出的结果和第一次有所区别