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

时间:2014-12-15 01:48:17

标签: c fork

我期待这段代码

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

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

4 个答案:

答案 0 :(得分:6)

你问:

  

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

代码存在问题,但不是您认为的问题。您对分叉过程如何工作的理解并不完全正确。

每个进程都有自己的进程内存空间副本。父进程的地址1444711088与子进程的地址1444711088不同。它们可以保持独立变化的值。

您的代码存在未定义的行为。

int* count = 0;

将指针初始化为0,这不是有效地址。你的意思是使用:

int count = 0;

代替?

答案 1 :(得分:2)

为了让这件事完全可以理解,我想引用以下资料。

  • C_Programming/POSIX_Reference/unistd.h/fork

      

    发出fork()系统调用时,所有页面的副本   对应父进程创建,加载到单独的进程中   操作系统为子进程提供的内存位置。但事实并非如此   在某些情况下需要。考虑一个孩子执行的情况   " EXEC" fork()后很快系统调用或退出。当。。。的时候   只需执行父进程的命令就需要子进程,   没有必要复制父进程'页面,自exec   替换用它调用它的进程的地址空间   要执行的命令。

         

    在这种情况下,使用称为写时复制(COW)的技术。同   这种技术,当发生fork时,父进程的页面不是   为子进程复制。而是在页面之间共享   孩子和父母的过程。每当一个过程(父母或孩子)   修改页面,单独创建该特定页面的单独副本   对于执行修改的那个过程(父母或孩子)。   然后,此过程将使用新复制的页面而不是   在以后的所有参考文献中共享另一个过程(那个过程   没有修改共享页面)继续使用原始副本   页面(现在不再共享)。这种技术被称为   copy-on-write,因为当某个进程写入页面时会复制该页面。

  • 另外,为了理解为什么这些程序似乎使用相同的内存空间(事实并非如此),我想引用本书的一部分"操作系统:原理与实践& #34;

      

    大多数现代处理器引入了间接级别,称为   虚拟地址。使用虚拟地址,每个进程的内存   从"同一"开始地方,例如,零。   尽管如此,每个进程都认为它拥有整个机器   显然事实并非如此。

    所以这些虚拟地址是物理地址的翻译,并不代表相同的物理内存空间,如果我们编译并运行多次显示方向的程序,我们可以做一个更实际的例子我们可以做一个测试一个静态变量,例如这个程序。

    #include <stdio.h>
    
    int main() {
        static int a = 0;
    
        printf("%p\n", &a);
    
        getchar();
    
        return 0;
    }
    

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

enter image description here

答案 2 :(得分:1)

进程可以在共享内存段中包含单个变量(或多个变量)。需要有一种方法来协调对变量的访问,以避免竞争条件。

如果您想实现此功能,请尝试以获取指导:

http://beej.us/guide/bgipc/output/html/singlepage/bgipc.html

答案 3 :(得分:1)

Fork and Exec

检查上面的分页进程/子进程的属性。请注意,其中一些包括:复制状态,包括打开文件,寄存器状态和所有内存分配。

基本上发生的是包括其堆栈和锁的进程被复制到新的地址空间。正如R Sahu指出的那样,地址似乎是相同的,但由于每个进程都有自己的虚拟内存副本,因此指针不指向同一位置(否则,如果任何进程可以在任何进程的内存空间中写入,那么将成为一个巨大的安全性和稳定性问题。更重要的是,这就是linux系统的工作方式。)

您正在寻找的是IPC解决方案,如消息传递或共享内存。

这里有点偏离主题:创建一个新进程涉及从父进程中分配一个子进程,以便为要加载到其上的其他程序的映像创建结构。