一直以来,在处理linux服务器的过程中,经常会遇到一个问题,有时候kill掉进程之后,端口被占用,新的进程一直起不来,等待很久也不能启动新进程,总是提示端口被占用,只有重启机器才能恢复正常,这个简直是个灾难,
查了一些资料,了解了如何查看端口占用情况和处理方法。
1、使用netstat命令查看端口占用情况。
2、我们还需要知道是什么程序占用,加上-p参数
3、如果比较多的时候我们也可以用grep过滤一下
4、然后kill掉占用端口的进程即可,正常情况下就可以启动新进程了
好了,这样我们就解决了令人烦恼的端口占用问题了。
不过我却遇到了另一个问题,我的代码使用了父子进程,kill掉父进程后,fork出来的子进程却占用了父进程监听的端口,必须把子进程也kill掉才能启动。
父进程监听一个端口后,fork出一个子进程,然后kill掉父进程,再重启父进程,这个时候提示端口占用,用netstat查看,子进程占用了父进程监听的端口。
原理其实很简单,子进程在fork出来的时候,使用了写时复制(COW,Copy-On-Write)方式获得父进程的数据空间、 堆和栈副本,这其中也包括文件描述符。刚刚fork成功时,父子进程中相同的文件描述符指向系统文件表中的同一项(这也意味着他们共享同一文件偏移量)。这其中当然也包含父进程创建的socket。
接着,一般我们会调用exec执行另一个程序,此时会用全新的程序替换子进程的正文,数据,堆和栈等。此时保存文件描述符的变量当然也不存在了,我们就无法关闭无用的文件描述符了。所以通常我们会fork子进程后在子进程中直接执行close关掉无用的文件描述符,然后再执行exec。
但是在复杂系统中,有时我们fork子进程时已经不知道打开了多少个文件描述符(包括socket句柄等),这此时进行逐一清理确实有很大难度。我们期望的是能在fork子进程前打开某个文件句柄时就指定好:“这个句柄我在fork子进程后执行exec时就关闭”。其实时有这样的方法的:即所谓 的 close-on-exec。
回到我们的应用场景中来,只要我们在创建socket的时候加上SOCK_CLOEXEC标志,就能够达到我们要求的效果,在fork子进程中执行exec的时候,会清理掉父进程创建的socket。
#ifdef WIN32
SOCKET ss = ::socket(PF_INET, SOCK_STREAM, 0);
#else
SOCKET ss = ::socket(PF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
#endif
当然,其他的文件描述符也有类似的功能,例如文件,可以在打开的时候使用O_CLOEXEC标识(
linux 2.6.23才开始支持此标记
),达到和上面一样的效果。或者使用系统的fcntl函数设置FD_CLOEXEC即可。
//方案A
int fd = open(“foo.txt”,O_RDONLY);
int flags = fcntl(fd, F_GETFD);
flags |= FD_CLOEXEC;
fcntl(fd, F_SETFD, flags);
//方案B,linux 2.6.23后支持
int fd = open(“foo.txt”,O_RDONLY | O_CLOEXEC);
好了,现在我们终于可以完美的解决端口占用这个令人烦恼的问题了。
转载:
http://blog.csdn.net/ljxfblog/article/details/41567419
http://blog.csdn.net/ljxfblog/article/details/41680115