最近测试人员反馈一个bug,大致的现象就是手动停止一个进程,结果该进程监听的端口被另外一个进程抢占并且监听。

虽然解决方案简单,但属于典型问题,在这里记录下,也算是给自己提个醒,同时有遇到类似问题的小伙伴,可以借鉴参考。

测试环境

测试环境是在 centos 7 下出现,但该问题属于通用问题,解决方案适用于 Linux 及其它类 Unix 系统。

现象分析

系统主进程启动时会创建 TCP 服务,监听固定端口8888,接着会通过 system 系统调用启动一个新的进程,示意代码如下:

int main()
{...//初始化listen(8888);  //创建socket监听服务system("start another process");  //通过系统调用system启动另外一个进程...//资源回收}

当出现进程端口被占用的问题时,我的第一反应就是两个进程有没有父子关系,在现场用 ps -ef 命令看了下,然后傻眼了,两个进程没有父子关系,居然有种失望的感觉。

还是不死心,接着回到研发环境查看源码,在主程序中没有发现 fork exec 这些显式创建子进程的地方,而是使用 system 启动的新进程,用法参见上面的示例代码。

到这里,问题的原因基本能确定了,进一步查看 system() 官方手册说明,

NAME
system - execute a shell command

SYNOPSIS
#include <stdlib.h>
int system(const char *command);
DESCRIPTION
system() executes a command specified in command by calling /bin/sh -c command, and returns after the command has been completed.
During execution of the command, SIGCHLD will be blocked, and SIGINT and SIGQUIT will be ignored.

RETURN VALUE
The value returned is -1 on error (e.g., fork(2) failed), and the return status of the command otherwise. This latter return status is in the format specified in wait(2). Thus, the exit code of the command will be WEXITSTATUS(status). In case /bin/sh could not be executed, the exit status will be that of a command that does exit(127).
If the value of command is NULL, system() returns nonzero if the shell is available, and zero if not.
system() does not affect the wait status of any other children.

果不其然,该系统调用会启动一个 /bin/sh shell环境运行命令,底层实现上是通过 fork 启动一个新的子进程(仔细看上面返回值那一段说明,特意对关键字 fork 加粗),这点可以编写测试程序,通过调用 getpid 获取进程ID验证父子关系,在这里就不再过多阐述。

问题原因

到这里,就可以解释开篇提到的进程间端口被抢占的问题,本质原因在于调用fork创建子进程后资源未能正确释放。

熟悉 Linux/Unix 系统编程的小伙伴,可能对 fork 系统调用不会陌生,fork 创建的子进程会复制父进程的数据空间、环境、文件描述符等资源,执行的代码也完全相同,可以说除了返回值不同,其它几乎与父进程一模一样。

一般在实际使用过程中,都是结合forkexec 一起使用,通过 fork 创建子进程,接着在子进程中调用 exec 系列函数替换现有进程,就能达到 fork 启动任意进程的目的。而在上面提到的代码片段中,我们的主程序中 system 启动其它进程的方式正是此类用法。

出现的原因就在于,调用 system 启动之前,父进程已经创建socket 并对其地址绑定具体监听端口;子进程会继承该socket描述符及其设置项,当父进程被主动关停后,操作系统释放其端口,子进程抢占该端口并完成监听。

解决方案

解决此问题的方法很简单,需要借助 fcntl 系统调用,在主进程代码中对 socket 描述符设置 FD_CLOEXEC 标志位,关闭 exec 启动的子进程中该 socket 的副本,代码片段如下:

int main()
{...//初始化sock = socket();  //创建套接字bind(sock, addr);  //绑定地址fcntl(scok, F_SETFD, FD_CLOEXEC);  //设置FD_CLOEXEC标志位listen(sock);      //监听服务system("start another process");  //通过系统调用system启动另外一个进程...//资源回收}

注意,为了说明 FD_CLOEXEC 标志如何设置,对 socket 监听服务的创建过程进行拆解,以便大家更好的理解。

总结

熟悉 Linux/Unix 编程的小伙伴,大家切记,在使用fork exec system等系列函数时,父进程已打开的资源,如果在子进程中未使用要注意正确的关闭,一方面能够避免资源浪费,另一方面避免出现本文类似的问题。

Linux进程间端口占用问题相关推荐

  1. linux 查看端口 程序,Linux查看程序端口占用情况

    今天发现服务器上Tomcat 8080端口起不来,老提示端口已经被占用. 使用命令: ps -aux | grep tomcat 发现并没有8080端口的Tomcat进程. 使用命令:netstat ...

  2. 深刻理解 Linux 进程间七大通信(IPC)

    前言 网络编程是 Linux C/C++的面试重点,今天我就来聊一聊进程间通信的问题,文章末尾列出了参考资料,希望帮助到大家. 篇幅有点长,希望大家耐心阅读. Linux 下的进程通信手段基本上是从 ...

  3. linux之查看端口占用

    第一章 linux之帮助命令 第二章 linux命令行快捷键 第三章 linux之防火墙 第四章 linux之服务开机自启 第五章 linux之关机与重启 第六章 linux之环境变量 第七章 lin ...

  4. linux共享内存示例,linux 进程间共享内存示例

    写入端: #include #include #include #include #include using namespace std; struct MappingDataType { int ...

  5. msgget();msgsnd();msgrcv();msgctl(); 消息队列 Linux进程间的通信方式之消息队列

    Linux进程间的通信方式 ----消息队列. 消息队列和共享内存类似 消息队列它允许一个或多个进程向它写消息,一个或多个进程向它写读消息. 消息队列存在于系统内核中,消息的数量受系统限制. 我们来看 ...

  6. Linux 进程间通讯(IPC)方式 ------- 共享内存

    Linux 进程间通讯(IPC)方式有以下几种: 1->管道(pipe)和有名管道(fifo). 2->消息队列 3->共享内存 4->信号量 5->信号(signal) ...

  7. Linux 进程间通讯方式 pipe()函数

    Linux 进程间通讯方式有以下几种: 1->管道(pipe)和有名管道(fifo). 2->消息队列 3->共享内存 4->信号量 5->信号(signal) 6-&g ...

  8. linux进程间信号量

    linux进程间信号量 Linux 提供的各种系统调用来实现一个具有两种状态的信号量(binary semaphore). 分配和释放 和用于分配.释放共享内存的 shmget 和 shmctl 类似 ...

  9. linux无名管道实验代码,Linux 进程间通讯之创建无名管道和读写无名管道

    Linux进程间通讯的方式: 1. 管道(pipe)和有名管道(FIFO). 2. 信号(signal) 3. 消息队列 4. 共享内存 5. 信号量 6. 套接字(socket) 管道通讯: 无名管 ...

最新文章

  1. TDD与FDD技术对比
  2. 常用 Linux 命令
  3. 4G EPS 中的随机接入
  4. 编程方法学6:操作符
  5. 神盾解密工具 之 解密 “ PHP 神盾解密工具 ”
  6. linux centos7杀进程,centos7 nginx 启动/进程状态/杀掉进程
  7. 如何在 Django REST Framework 中对分页结果过滤和排序?
  8. java string转number_DataBinding的简单使用(java/kotlin)
  9. js获取当前html路径,JavaScript获取当前url根目录(路径)
  10. isupper()函数
  11. ffmpeg 给视频添加字幕,在视频的某个时间段加入声音特效和动画
  12. java抽奖系统的设计参考文献,抽奖系统的设计与实现论文范文论文
  13. Codeforces 300D Painting Square dp
  14. 程序猿健身之腹肌~基本版本
  15. 2019最新Web全栈架构师第九期视频教程全套
  16. mysql 多表查询语句
  17. Laravel Redis的使用
  18. 看电影 --《山楂树之恋》
  19. 从0开始复健——C的枚举
  20. 【Ethercat机器人控制系统开发】倍福Twincat入门教程

热门文章

  1. 中国防近视镜片市场深度研究分析报告
  2. 最新版都叫兽数据恢复软件和注册机
  3. pdca实施的流程图_PDCA方案应用.ppt
  4. ONNX模型tensor shapes inference和Flops统计工具
  5. 【博弈论】耶鲁大学公开课--博弈论Problem Set 3--Solution
  6. unable to get repr for class 'torch.tensor'
  7. 计算机图形学 实验7 《复杂图形绘制-Bezier曲线与Hermite曲线》
  8. 一步一步学习Vim 全图解释
  9. HDU2570 贪心
  10. 数据科普:期权价格和相关变量的关系(投资必知必会)