我在等这个代码
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
你能说出这段代码的问题在哪里吗?
5条答案
按热度按时间3hvapo4f1#
你问:
你能说出这段代码的问题在哪里吗?
代码有问题,但不是你认为的地方。你对分叉进程如何工作的理解不太正确。
每个进程都有自己的进程内存空间副本。父进程的地址
1444711088
与子进程的地址1444711088
不同。它们可以保存独立变化的值。您的代码可能会出现未定义的行为。
它初始化指向
0
的指针,该指针不是有效地址。是否要使用:而不是
xmd2e60i2#
为了使这件事完全可以理解,我想引用以下来源。
当叉子()系统调用时,将创建与父进程对应的所有页的副本,并由子进程的操作系统加载到单独的内存位置。但在某些情况下不需要这样做。请考虑子进程执行“exec”系统调用或在分叉后很快退出的情况()。当需要子进程只是执行父进程的命令时,不需要复制父进程的页面,因为exec用要执行的命令替换了调用它的进程的地址空间。
在这种情况下,一种称为写入时复制的技术(COW)。使用此技术,当发生派生时,父进程的页不会复制给子进程。相反,页在子进程和父进程之间共享。每当进程(父或子)修改页面,为该进程单独制作该特定页面的单独副本(父代或子代)。然后,此进程将在将来的所有引用中使用新复制的页面,而不是共享的页面。另一个进程(未修改共享页的页)继续使用该页的原始副本(现在不再共享)。这种技术称为写入时复制,因为当某个进程写入该页时,该页被复制。
大多数现代处理器都引入了一个称为虚拟地址的间接层。使用虚拟地址,每个进程的内存都从“相同”的位置开始,例如零。每个进程都认为它拥有整个机器,尽管事实显然并非如此。
所以这些虚拟地址是物理地址的转换,并不代表相同的物理内存空间,为了留下一个更实际的例子,我们可以做一个测试,如果我们编译并多次运行一个显示静态变量方向的程序,比如这个程序。
如果我们直接处理物理内存,在两个不同的程序中获得相同的内存地址是不可能的,但是运行程序几次得到的结果是这样的,
rdlzhqv93#
进程在共享内存段中可以有一个变量(或多个变量),需要有一种方法来协调对变量的访问,以避免争用情况。
如果您想实现这一点,请尝试以下指导:
http://beej.us/guide/bgipc/output/html/singlepage/bgipc.html
apeeds0o4#
Fork and Exec
检查上一页中派生进程/子进程的属性。请注意,其中一些属性包括:复制状态,包括打开的文件、寄存器状态和所有内存分配。
基本上发生的是进程包括它的栈和锁被复制到一个新的地址空间,正如RSahu指出的,地址看起来是相同的,但是由于每个进程都有它自己的虚拟内存副本,指针并不指向相同的位置(否则如果任何进程都可以在任何进程的内存空间中写入,将成为巨大的安全性和稳定性问题,更重要的是,Linux系统就是这样工作的。)
您正在寻找的是消息传递或共享内存等IPC解决方案。
有点跑题了:新进程的创建涉及从父进程分叉子进程,以便创建用于要加载到其上的其它程序的映像的结构。
zpf6vheq5#
你在写
其将0分配给该地址。
如果要将值0赋给地址计数处的值,请执行以下操作