UNIX/LINUX fork函数的问题 并不适合共享

这篇具有很好参考价值的文章主要介绍了UNIX/LINUX fork函数的问题 并不适合共享。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

起因介绍

一位朋友问我一个关于socket通信的相关问题,其需要解决的问题如下:

需要存在一个服务器进程,服务器进程会进行监听,负责建立与客户端的socket连接,同时可以存在多个客户端进程,客户端进程之间可以进行通信,不过客户端之间并不会建立socket连接,通信是通过将信息发送给服务端进程,服务端进程查找与目标客户端建立的socket标志来进行信息的发送。

为了解决上面的问题,这位朋友在服务端进程中自定义了一个链表用于保存与服务器建立的socket连接的客户端名称以及对应的socket标识符,当成功建立连接的时候,则在链表中插入一个元素,当一个客户端发送退出指令的时候,则将对应的socket关闭并将信息从链表中删除,每次有客户端需要发送数据的时候,则需要遍历链表,然后找到目标socket进行通信。

同时为了实现通信的要求,则必须要使用多进程或者多线程,这位朋友在服务端的代码中每次监听成均通过fork来建立一个子进程来处理与某个客户端之间的通信,同时这位朋友,在代码中申请了一块共享内存,用于保存链表头。

出现的问题

上述的描述似乎是很合理的,但是他在运行后出现了一个奇怪的现象:当服务端进程启动后,同时启动两个客户端进程与服务端进程建立socket连接,当client1client2发送消息的时候,代码出现了死循环。

哪里出现了死循环呢?根据朋友的调试,发现死循环出现在在链表中查询目的socket的过程,他发现,链表变成了一个环,同时这个链表上只有一个结点,也就是只有自己的结点信息,导致一直在链表上进行循环。

问题的原因

为什么会出现死循环呢?出现问题的关键就在于他使用fork这个函数。为了更加简单的进行描述我们先进性一个简单的实验:

编写一个程序,同时创建一个变量,对其进行赋值,然后调用fork()查看是否父子进程均能获取变量的正确值,同时尝试在子进程中对变量的值进行修改,父进程等待子进程完成后查看变量的值,看一下值是否成功被修改。

注:上面的问题实际上来自于《操作系统导论》,非常好的一本书,推荐大家阅读。

可以很容易的写出上面实验的代码:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

int main()
{
    int x = 137;
    int ret = fork();
    if (ret < 0) {
        printf("one error occurs when fork(), the ret: %d.\n", ret);
        exit(ret);
    } else if (ret == 0) {
        printf("child process (%d), the value of x is: %d.\n", getpid(), x);
        x *= 2;
    } else {
        printf("parent process (%d), the value of x is: %d.\n", getpid(), x);
        ret = wait(NULL);
        printf("after child process write the varible, parent process (%d), the value of x is: %d.\n", getpid(), x);
    }
    return 0;
}

运行结果如下:

parent process (110), the value of x is: 137.
child process (111), the value of x is: 137.
after child process write the varible, parent process (110), the value of x is: 137.

可以看到子进程对于变量的修改父进程并不知道。这就是问题所在:通过fork创建的进程会复制父进程的所有信息(注意是复制而不是共享),一个通过fork创建的进程对变量的修改对于另一个进程不可见。

上面说的复制,指的是将进程完整的拷贝一遍,放到另一块内存区域中进行执行,这会有一个问题也就是,初始两个进程所有的值都是一样的,同时虚拟地址也是一样的(这一点体现在如果两个进程紧接着进行堆内存的申请,那么会在会获得同样的地址,这一点也很好理解,由于是复制,那么自然堆的状态也是一样的,获取到同样的虚拟空间也是合理地,但是实际的物理空间却不相同)。

有了上面的基础,我们再来看思考一下为什么会出现死循环?

问题的关键在于同时使用了共享内存和fork,使用的共享内存能够保证在子进程中进行修改,其他进程能够看见,我们来实际进行模拟一下:

  • 首先,服务端进程进行启动,申请共享内存,地址我们记为0,用headp_address变量保存该地址(即headp_address=0)由于此时的链表是空的,表头初始为空,即执行*headp_address=NULL
  • client1启动,此时向链表中插入一个新的结点,新结点通过malloc进行申请内存,假设此时申请的地址为4那么我们将该节点插入到链表头即执行*headp_address=new_node,此时new_node=4
  • client2启动,此时同样进行新结点内存的申请(通过malloc),根据之前的结论,此时会申请到相同的内存即还是会申请到4(这里是虚拟内存),但是由于对于共享内存上的修改,两个进程能够发现,此时发现链表中已经有一个元素了,于是使用头插法将当前的结点插入到链表的开头,也就是执行:
    new_node->next = *headp_address;
    *headp_address=new_node;
    
    根据前面的信息我们知道*headp_address=4,在执行上面的语句之后,我们发现*headp_address=4, *headp_address->next=4;也就是说这个时候链表变成了一个环,这也就是为什么后面通过某个客户端进行信息的发送的时候会出现死循环。

问题的解决

这里提供几种解决方案:文章来源地址https://www.toymoban.com/news/detail-594446.html

  • 使用pthread替换fork()pthread可以实现子线程进行修改各个进程能够看到,也就是说其更偏向与实现共享的功能,而fork()则是偏向于复制。(实际上这两个函数都调用了系统调用clone,但是pthread_create在调用的时候增加了CLONE_VM标志,使得能够实现共享)。
  • 如果一定要使用fork()则应该通过共享内存实现共享,也就是链表的申请与释放也应该在共享内存上进行,这个时候则应该使用静态链表进行管理(也就是固定空间大小的链表)。
  • 如果要同时使用fork()和动态链表,我并没有想到什么比较好的方法。

到了这里,关于UNIX/LINUX fork函数的问题 并不适合共享的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unix和Linux常用的命令(不断积累)

    chmod 是一个Unix和Linux操作系统中的命令,它 用于改变文件或目录的权限 。在Linux中,每个文件或目录的权限可以被设定为三个不同的用户级别: 文件所有者 , 所有者所在的用户组 ,以及 其他用户 。 chmod 的基本语法如下: 在这个命令中: options :这是可选参数,主要用来

    2024年02月10日
    浏览(40)
  • Linux和UNIX的关系及区别(详解)

    UNIX 与 Linux 之间的关系是一个很有意思的话题。在目前主流的服务器端操作系统中,UNIX 诞生于 20 世纪 60 年代末,Windows 诞生于 20 世纪 80 年代中期,Linux 诞生于 20 世纪 90 年代初,可以说 UNIX 是操作系统中的\\\"老大哥\\\",后来的 Windows 和 Linux 都参考了 UNIX。 二者的关系,不是大

    2024年02月05日
    浏览(27)
  • Unix/Linux编程:UDS 流(Stream)

    socket 是一种 IPC (Inter-Process Communication,进程间通信)方法,它允许位于同一主机(计算机)或使用网络连接起来的不同主机上的应用程序之间交换数据。通过使用Socket,开发人员可以创建网络应用程序,使其能够通过网络进行数据交换和通信。 Socket API通常用于基于TCP/IP协

    2024年02月08日
    浏览(39)
  • 【Linux专区】 Linux is not unix | Linux发展史 | Linux应用现状

    💞💞 欢迎来到 Claffic 的博客 💞💞      👉  专栏 : 《Linux专区》👈         前言: 上次提前带大家搭建了Linux的环境,其实之前应该还有一步的,就是向大家介绍Linux发展史,毕竟如此伟大的产品,不懂Linux史就学Linux总觉得有点奇怪... ...   本文入选全站综合热榜第

    2024年02月06日
    浏览(45)
  • Unix/Linux 中的软链接和硬链接

    UNIX 中的链接是指向文件的指针。与任何编程语言中的指针一样,UNIX 中的链接是指向文件或目录的指针。创建链接是一种访问文件的快捷方式。链接允许多个文件名在别处引用同一个文件。 有两种类型的链接: 软链接或符号链接 硬链接 当链接的源(链接到的内容)被移动

    2024年02月04日
    浏览(36)
  • 操作系统的最强入门科普(Unix/Linux篇)

    大家好,我是小枣君。 今天这篇文章,我们来聊聊 操作系统( Operating System ) 。 说到操作系统,大家都不会陌生。我们天天都在接触操作系统——用台式机或笔记本电脑,使用的是windows和macOS系统;用手机、平板电脑,则是android(安卓)和iOS系统。 如果是从事信息通信行

    2024年02月04日
    浏览(60)
  • 用Visual Studio(VS)开发UNIX/Linux项目

    目录 FTP是免不了的 正确设置头文件 组织项目结构 创建何种项目类型 FTP自动上传         大部分具有Windows开发经验的程序员会比较喜欢使用Visual Studio,而大部分Unix/Linux程序员则喜欢使用UltraEdit直接在主机上写代码。         为什么直接在主机上写代码呢,因为主机是没有

    2024年02月08日
    浏览(44)
  • Linux/Unix-CPU-SuperPI-Unixbench性能测试

    测试服务器CPU单核及多核SuperPI圆周率测试real和user值,SuperPI是利用CPU的浮点运算能力来计算出π(圆周率),测试系统稳定性和测试CPU计算完后特定位数圆周率所需的时间;及Unixbench单核及多核测试Index得分,测试方法如下: 类型 预期结果 测试步骤 SuperPI测试(单核) real和u

    2024年02月08日
    浏览(42)
  • export 是一个在 Unix 和类 Unix 系统(比如 Linux 和 macOS)中常用的 shell 命令,主要用于设置或导出环境变量。

    export 是一个在 Unix 和类 Unix 系统(比如 Linux 和 macOS)中常用的 shell 命令,主要用于设置或导出环境变量。环境变量是在操作系统中用于存储系统设置和命令行程序配置的全局值。下面提供了一些 export 命令的基本用法和示例。 基本用法 设置环境变量 : 这里, VARIABLE_NAME 是

    2024年01月19日
    浏览(47)
  • 一文搞清UNIX/Linux与Windows文件换行符格式差异

    当一个文件在Windows和Linux上交替操作后,经常遇到一些莫名其妙的问题,如shell脚本无法执行,找不到shell脚本等问题,本文j谨就这一问题做一总结,供各位参考; 本博客地址,https://blog.csdn.net/qxhgd,欢迎各位关注,转发请注明出处。 换行符是行尾 (EOL),是一个特殊的字

    2024年02月15日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包