-
通过
fork
函数创建的子进程能够继承父进程中所有变量的值,但是一旦创建后,父进程和子进程中的变量不再有任何关系,即使是一个全局变量,父进程和子进程都会有不同的副本,修改并不会影响另一个进程中的这个全局变量的值; -
产生僵尸进程的原因:
- 子进程使用
exit()
函数返回一个返回码(即exit()
函数里面传入了实参); - 子进程main函数(每个进程最后都会从main函数正常返回的啦)通过return语句返回一个值。
其根本原因是上述两种情况都会将返回的值传给操作系统,而操作系统不会销毁子进程,直到把这些值传递给产生该子进程的父进程(更准确的说是在父进程未收到子进程的返回值前,子进程的进程号的信息仍然保留在系统的进程表里面)。处在这个状态下的进程就是僵尸进程。也就是说,将子进程变成僵尸进程的正是操作系统;
- 子进程使用
-
如何销毁僵尸进程:
- 通过在父进程里面调用
wait()
函数,来等待子进程的结束并获取子进程的返回值。这是阻塞函数; - 通过在父进程里循环调用
waitpid()
函数,来获取指定子进程的返回值或者任意子进程的返回值。此函数可阻塞也可非阻塞; - 通过使用
signal()
或sigaction()
来注册子进程销毁时的信号处理函数,然后在信号处理函数里面使用waitpid()
来销毁对应的僵尸进程; - 使用双
fork()
的方式,将真正使用的子进程创建为父进程的孙子进程,然后将父进程的儿子进程使用waitpid()
销毁,这样,孙子进程的父进程就会变成init进程。当此孙子进程结束时,init进程会自动调用相关的wait函数,来销毁对应的僵尸进程。
- 通过在父进程里面调用
-
wait()
与waitpid()
异同:这两个函数都可用来阻止僵尸进程的存在。
wait()
是阻塞函数,waitpid()
可以以非阻塞方式运行;wait()
不能指定子进程,只能等待任意的子进程结束。waitpid()
可以指定销毁哪个子进程对应的僵尸进程;wait()
成功时返回已结束的子进程ID,失败时返回-1。waitpid()
成功时返回已结束的子进程的ID或者0(0表示没有任何子进程结束或者指定的子进程还未结束),失败时返回-1。
-
signal()
与sigaction()
异同:两个都是注册信号处理函数的函数。
signal()
的兼容性没有sigaction()
强,建议使用后者,前者很少用了;- 前者使用简单,后者使用需要定义一个
sigaction
结构体; - 它们都可以获取之前注册的信号处理函数的指针,前者使用返回值的方式获取,后者通过最后一个新参的方式获取(不想获取时传入nullptr)。
-
如果一个套接字的文件描述符被复制到多个进程空间中,则每个进程空间都要把它复制的那一份文件描述符给close掉,否则,最后对应的套接字无法销毁(就像是加了引用计数一样,只有当套接字的引用计数为0时才销毁套接字)。此外,只有当套接字真正被销毁时,才会给对方发送
EOF
,如果一个套接字被多个文件描述符所共享,则只close其中一个副本,不会发送EOF
,只有最后一个指向此套接字的文件描述符被close时,才会发送EOF
。此外,可以直接通过调用shutdown()
函数来显式的发送EOF
; -
通过
fork()
复制的socket文件描述符指向同一个socket,socket本身并没有被复制。同样,通过fork()
复制的管道文件描述符也对应着同一对管道,管道本身并没有被复制。它们都属于操作系统的,不属于进程空间的。此外,子进程中应该主动关闭被赋值过来的但是不需要的文件描述符。
TCP\IP网络编程学习笔记(四)
最新推荐文章于 2023-05-06 19:59:39 发布