Вилка - одинаковые адреса памяти?
Это о C в Linux.
У меня fork()
в main()
, где я создаю 2 дочерних процесса. Затем в обоих дочерних процессах запустите функцию abc()
, где есть локальная переменная x
. Я пишу в нем некоторое значение. Затем я печатаю адрес этой переменной с помощью printf("%p",&x)
.
Оба процесса распечатывают SAME адрес. Я думал, что каждый ребенок получает (независимую) копию родительской памяти. Мне нужно, чтобы каждый процесс имел свою переменную x
. Как я могу это сделать или делаю что-то неправильно?
Ответы
Ответ 1
Вам нужно понять, что существует разрыв между физической памятью и виртуальным адресным пространством процесса.
Каждый процесс получает свое собственное виртуальное адресное пространство 4G, и это задача операционных систем и менеджеров аппаратной памяти для сопоставления ваших виртуальных адресов с физическими.
Итак, хотя может показаться, что два процесса имеют один и тот же адрес для переменной, это только виртуальный адрес.
Менеджер памяти отобразит это на совершенно другой физический адрес a.
Это сопоставление также позволяет запускать десять процессов, каждый из которых занимает 1G, даже если ваш компьютер имеет только 4G физической памяти. ОС может поменять местаки памяти на диск и вернуть их обратно, когда вы попытаетесь их использовать.
a: В основном это верно. Он может отображаться на тот же физический адрес, если вы делитесь друг с другом между процессами. Например, общая память, код ядра и данные, динамические библиотеки и т.д.
Ответ 2
Если вы перестанете думать минутку, было бы невозможно, чтобы fork
передавал переменным отдельные адреса в родительском и дочернем процессах. Вы уже могли хранить адреса в любом месте в памяти или хэшировать их или сохранять их в файл или что-либо еще, а затем все, что было в ребенке, зависящее от того, что эти адреса действительны, будет ужасно ломаться. Фактически fork
делает и должен создать дочерний процесс, в котором виртуальное адресное пространство идентично виртуальному адресному пространству родителя.
Ответ 3
Из-за системы виртуальной памяти каждый дочерний процесс имеет свою собственную переменную с тем же (виртуальным) адресом.
Те же виртуальные адреса не будут указывать на то же физическое местоположение.
Ответ 4
Чтобы понять, как это может произойти, вам нужно понять модель процесса/потока Linux. Linux следует за моделью fork-and-exec, унаследованной от UNIX. Процесс, порожденный системным вызовом fork() в этой модели, представляет собой нечто среднее между потоком и процессом Windows.
Когда поток генерируется (не имеет значения в Linux или Windows), новый поток разделяет его адресное пространство с родительским. Оба могут найти одни и те же объекты, обратившись к тем же адресам. Но эти потоки используют разные стеки. В результате локальные переменные обоих потоков гарантированно не будут иметь одинаковые адреса.
Когда процесс запускается в среде Windows, ОС строит совершенно новое адресное пространство с нуля и заполняет его памятью и данными. Теоретически, локальная переменная обоих процессов может иметь одинаковые адреса, но на практике вероятность этого будет очень низкой. И даже в том случае, когда обе переменные будут использовать один и тот же адрес, эти две переменные все равно будут разными объектами.
Процессы UNIX имеют сходство с потоками и процессами Windows. Как и в случае второго, ОС создаст новое адресное пространство для дочернего процесса, но в отличие от Windows, Linux создает его путем ленивого копирования адресного пространства родительского процесса с использованием подхода Copy-On-Write (COW). COW означает, что оба процесса будут использовать одну и ту же память, но до момента, когда один из них изменит ее. В момент попытки записать в память ОС снова будет задействована, чтобы скопировать фрагмент памяти, который будет изменен, и назначить одну копию родительскому, а другой - дочернему. Начиная с этого момента каждый процесс будет работать со своей независимой копией объектов в модифицированном блоке памяти, но они все равно будут иметь одинаковые адреса. То же самое верно для хранимых на стеке и локальных переменных.
В вашем случае у вас есть два ребенка с двумя копиями одного и того же стека, по которым локальные переменные хранятся на тех же адресах, но в разных адресных пространствах. Затем вы запускаете тот же код для обоих дочерних элементов. Другими словами, у вас одинаковое начальное состояние макета стека и запуск того же кода, который так же изменяет этот макет. В результате вы будете иметь те же локальные переменные, которые расположены по тем же адресам.
Ответ 5
так как вы печатаете адрес переменной стека (локальная переменная). его адрес будет таким же (не важно, чтобы вы обновили его значение или нет). поскольку оба процесса имеют общий виртуальный стек.
но если вы пытаетесь напечатать адрес глобальной переменной внутри общей функции (вызванной из родительского и дочернего процесса), то ее адрес будет таким же, пока вы не обновите его.
если процесс обновляет значение глобальной переменной, тогда этот процесс будет иметь уникальную копию (посредством копирования на механизм записи).