C语言 在分叉进程上使用相同的变量

yxyvkwin  于 2023-01-25  发布在  其他
关注(0)|答案(5)|浏览(119)

我在等这个代码

void func(int* count)
{
    *count += 1;

    char* buf[100];
    sprintf(buf, "%d -> %d\n", count, *count);
            write(1, buf, strlen(buf));
}
int main()
{
    int* count = 0;
    int pid = fork();
    if(pid == 0)
    {
        func(&count);
    }
    else
    {
        func(&count);
    }

    return 0;
}

将打印

1444711088 -> 1
1444711088 -> 2

因为2个fork使用相同的内存单元(1444711088)用于count变量,当其中一个修改变量的值时,另一个将受到影响。但它没有按预期工作。它打印了以下内容:

1444711088 -> 1
1444711088 -> 1

你能说出这段代码的问题在哪里吗?

3hvapo4f

3hvapo4f1#

你问:
你能说出这段代码的问题在哪里吗?
代码有问题,但不是你认为的地方。你对分叉进程如何工作的理解不太正确。
每个进程都有自己的进程内存空间副本。父进程的地址1444711088与子进程的地址1444711088不同。它们可以保存独立变化的值。
您的代码可能会出现未定义的行为。

int* count = 0;

它初始化指向0的指针,该指针不是有效地址。是否要使用:

int count = 0;

而不是

xmd2e60i

xmd2e60i2#

为了使这件事完全可以理解,我想引用以下来源。

当叉子()系统调用时,将创建与父进程对应的所有页的副本,并由子进程的操作系统加载到单独的内存位置。但在某些情况下不需要这样做。请考虑子进程执行“exec”系统调用或在分叉后很快退出的情况()。当需要子进程只是执行父进程的命令时,不需要复制父进程的页面,因为exec用要执行的命令替换了调用它的进程的地址空间。
在这种情况下,一种称为写入时复制的技术(COW)。使用此技术,当发生派生时,父进程的页不会复制给子进程。相反,页在子进程和父进程之间共享。每当进程(父或子)修改页面,为该进程单独制作该特定页面的单独副本(父代或子代)。然后,此进程将在将来的所有引用中使用新复制的页面,而不是共享的页面。另一个进程(未修改共享页的页)继续使用该页的原始副本(现在不再共享)。这种技术称为写入时复制,因为当某个进程写入该页时,该页被复制。

  • 此外,为了理解为什么这些程序似乎使用相同的内存空间(事实并非如此),我想引用《操作系统:原则与实践”。

大多数现代处理器都引入了一个称为虚拟地址的间接层。使用虚拟地址,每个进程的内存都从“相同”的位置开始,例如零。每个进程都认为它拥有整个机器,尽管事实显然并非如此。
所以这些虚拟地址是物理地址的转换,并不代表相同的物理内存空间,为了留下一个更实际的例子,我们可以做一个测试,如果我们编译并多次运行一个显示静态变量方向的程序,比如这个程序。

#include <stdio.h>

int main() {
    static int a = 0;

    printf("%p\n", &a);

    getchar();

    return 0;
}

如果我们直接处理物理内存,在两个不同的程序中获得相同的内存地址是不可能的,但是运行程序几次得到的结果是这样的,

rdlzhqv9

rdlzhqv93#

进程在共享内存段中可以有一个变量(或多个变量),需要有一种方法来协调对变量的访问,以避免争用情况。
如果您想实现这一点,请尝试以下指导:
http://beej.us/guide/bgipc/output/html/singlepage/bgipc.html

apeeds0o

apeeds0o4#

Fork and Exec
检查上一页中派生进程/子进程的属性。请注意,其中一些属性包括:复制状态,包括打开的文件、寄存器状态和所有内存分配。
基本上发生的是进程包括它的栈和锁被复制到一个新的地址空间,正如RSahu指出的,地址看起来是相同的,但是由于每个进程都有它自己的虚拟内存副本,指针并不指向相同的位置(否则如果任何进程都可以在任何进程的内存空间中写入,将成为巨大的安全性和稳定性问题,更重要的是,Linux系统就是这样工作的。)
您正在寻找的是消息传递或共享内存等IPC解决方案。
有点跑题了:新进程的创建涉及从父进程分叉子进程,以便创建用于要加载到其上的其它程序的映像的结构。

zpf6vheq

zpf6vheq5#

你在写

int* count = 0;

其将0分配给该地址。
如果要将值0赋给地址计数处的值,请执行以下操作

int* count = new int;
*count = 0;

相关问题