Почему компиляторы создают одну переменную «дважды»? - PullRequest
1 голос
/ 01 июля 2010

Я знаю, что это более «тяжелый» вопрос, но я думаю, что он тоже интересен.Это было частью моих предыдущих вопросов о функциях компилятора , но тогда я объяснил это очень плохо, и многие ответили только на мой первый вопрос, так что это так:

Итак, если мои знанияверно, современные системы Windows используют подкачку как способ переключения задач и обеспечения того, чтобы каждая задача имела свое место в памяти.Таким образом, каждый процесс занимает свое место, начиная с 0.

Когда многозадачность вступает в силу, Kernel должен сохранить все важные регистры в стеке задачи, как я считаю, чем сохранить текущий указатель стека, изменить запись страницы напереключение на физическое адресное пространство другого процесса, загрузка нового указателя стека процессов, извлечение сохраненных регистров и продолжение путем вызова адреса указателя всплывающей инструкции.

Из-за этой приятной функции (подкачки) каждый процесс считает, что у него хорошая квартирапамять в пределах досягаемости.Таким образом, нет дальних переходов, дальних указателей, сегмента памяти или сегмента данных.Все хорошо и линейно.

Но, когда для процесса больше нет сегментации, почему все еще компиляторы создают переменные в стеке или когда глобальны непосредственно в другом пространстве памяти, чем непосредственно в программном коде?

Позвольте мне привести пример, у меня есть код C: int a=10;

, который переводится в (синтаксис Intel): mov [position of a],#10

Но чем, на самом деле вы занимаетесь?больше байтов в оперативной памяти, чем необходимо.Поскольку первые несколько байтов принимают инструкцию actall, а после выполнения этой инструкции появляется новый байт, содержащий значение 10.

Почему вместо этого нет необходимости переключать какой-либо сегмент (таким образом,скорость процесса) - это не просто значение 10, закодированное непосредственно в программу, например:

xor eax,eax //just some instruction<br> 10 //the value iserted to the program<br> call end //just some instruction

Поскольку компилятор знает точное положение каждой инструкции при работе с этой переменной,он просто использовал бы его адрес.

Я знаю, что переменные const делают это, но на самом деле они не являются переменными, когда вы не можете их изменить.

Надеюсь, я хорошо изложил свой вопрос, но я все еще изучаю английский, так что простите мои синтаксические и даже семантические ошибки.

РЕДАКТИРОВАТЬ:

Я прочитал ваши ответы, и, похоже, основываясь на них, я могу изменить свой вопрос:

Итак, кто-то сказал здесь, что глобальная переменная на самом деле - это та часть значений, которая прикреплена непосредственно к программе, я имею в виду, когда переменная глобальная, является ли онаd до конца программы, или просто созданный как локальный во время выполнения, но вместо того, чтобы быть в стеке в куче напрямую?

Если первый случай - привязан к самой программе, то почемусуществование локальных переменных?Я знаю, вы скажете мне из-за рекурсии, но это не так.Когда вы вызываете функцию, вы можете поместить любое пространство памяти в стек, поэтому там нет никакой программы.

Надеюсь, вы меня понимаете, использование памяти всегда неэффективно, когда какое-то значение (даже 0)созданный в стеке из некоторой инструкции, потому что вам нужно место в программе для этой инструкции, а не для фактической переменной.Вот так: push #5 //instruction that says to create local variable with integer 5 И затем эта инструкция просто делает номер 5 в стеке.Пожалуйста, помогите мне, я действительно хочу знать, почему это так.Спасибо.

Ответы [ 6 ]

5 голосов
/ 01 июля 2010

Рассмотрим:

  • локальные переменные могут иметь более одного одновременного существования, если подпрограмма вызывается рекурсивно (даже косвенно, скажем, в рекурсивном приличном парсере) или из более чем одного потока, и эти случаи происходят в том же контекст памяти
  • пометка памяти программы как недоступной для записи, а стек + куча как неисполняемый - небольшая, но полезная защита от определенных классов атак (разрушение стека ...), а - , используемый некоторыми ОС ( Я не знаю, если Windows делает это, однако)

Ваше предложение не учитывает ни один из этих случаев.

4 голосов
/ 01 июля 2010

Таким образом, нет дальних переходов, дальних указателей, сегмента памяти или сегмента данных.Все красиво и линейно.

Да и нет.Различные программные сегменты имеют разные цели - несмотря на то, что они находятся в плоской виртуальной памяти.Например, сегмент данных доступен для чтения и записи, но вы не можете выполнить данные.Сегмент кода является читаемым и исполняемым, но вы не можете писать в него.

почему компиляторы все еще создают переменные в стеке, [...] чем непосредственно в программном коде?

Простой.

  1. Сегмент кода недоступен для записи.По соображениям безопасности в первую очередь.Во-вторых, большинству процессоров не нравится, когда в код записывается сегмент кода, поскольку это нарушает многие существующие оптимизации, используемые для ускорения выполнения.
  2. Состояние функции должно быть приватным для функции из-за таких вещей, как рекурсия и многократное использование.

- это не просто значение 10, закодированное непосредственно в программу, подобную этой

Современные инструкции по предварительному выбору процессоров для таких вещей, как параллельное выполнение и выходвыполнение заказа.Помещение мусора (в процессор, который является мусором) в сегмент кода просто уменьшит (или полностью отменит) эффект методов.И они несут ответственность за львиную долю прироста производительности ЦП за последнее десятилетие.

, когда нет необходимости переключать какой-либо сегмент

Так что, если естьнет затрат на переключение сегмента, зачем тогда помещать это в сегмент кода?Нет проблем хранить его в сегменте данных.

Особенно в случае сегмента данных только для чтения имеет смысл поместить все данные программы, доступные только для чтения, в одно место - так как они могут использоваться совместно.все экземпляры запущенного приложения, экономящие физическую оперативную память.

Поскольку компилятор знает точное положение каждой инструкции, при работе с этой переменной он просто использует ее адрес.

Нет, не совсем.Большая часть кода является перемещаемой или независимой от позиции.Код исправлен с реальными адресами памяти, когда ОС загружает его в память.На самом деле специальные методы используются для того, чтобы на самом деле избежать исправления кода, чтобы сегмент кода также мог быть общим для всех запущенных экземпляров приложения.

ABI отвечает за определение того, как и какой компилятор и компоновщикПредполагается, что для выполнения программы исполняющей ОС.Я не видел Windows ABI, но ABI, используемые Linux, легко найти: ищите «AMD64 ABI».Даже чтение Linux ABI может ответить на некоторые ваши вопросы.

3 голосов
/ 01 июля 2010

Почему это не просто значение 10, закодированное непосредственно в программу, например:

xor eax,eax //just some instruction  
10 //the value iserted to the program  
call end //just some instruction  

То, что - это способ хранения глобальных переменных. Однако вместо того, чтобы застрять в середине исполняемого кода (который является грязным, и даже невозможен в настоящее время), они хранятся сразу после кода программы в памяти (в Windows и Linux, в минимум) , в так называемом разделе .data .

Когда это возможно, компилятор перемещает переменные в раздел .data для оптимизации производительности. Однако есть несколько причин, по которым это может не произойти:

  • Некоторые переменные нельзя сделать глобальными, включая переменные экземпляра для класса, параметры, передаваемые в функцию (очевидно), и переменные, используемые в рекурсивных функциях.
  • Переменная еще где-то существует в памяти, и для доступа к ней по-прежнему должен быть код . Таким образом, использование памяти не изменится. На самом деле, на x86 («Intel»), согласно этой странице инструкция для ссылки на локальную переменную:

    mov eax, [esp+8]
    

    и инструкция для ссылки на глобальную переменную:

    mov eax, [0xb3a7135]
    

    оба имеют такт 1 ( один! ).

    Единственное преимущество заключается в том, что если каждая локальная переменная является глобальной, вам не придется освобождать место в стеке для локальных переменных.

  • Добавление переменной в сегмент .data может фактически увеличить размер исполняемого файла, поскольку переменная фактически содержится в самом файле.

  • Как caf упоминает в комментариях , переменные на основе стека существуют только во время работы функции - глобальные переменные занимают память в течение всего выполнения программы .

3 голосов
/ 01 июля 2010

То, о чем вы говорите, это оптимизация , и это дело компилятора.Если ничто не изменит это значение, и компилятор сможет это выяснить, то компилятор совершенно свободен делать то, что вы говорите (если a не объявлено volatile).

Теперь, если вы говоритечто вы видите, что компилятор не делает это, и вы думаете, что это должно произойти, вам придется поговорить с вашим автором компилятора.Если вы используете VisualStudio, их адрес - One Microsoft Way, Redmond WA.Удачи стучится в двери там.: -)

0 голосов
/ 01 июля 2010

Переменные имеют место для хранения и могут быть изменены.Не имеет смысла вставлять их в сегмент кода, где они не могут быть изменены.

Если у вас есть код с int a=10 или даже const int a=10, компилятор не может преобразовать код, который ссылается на «a», для использованияконстанта 10 напрямую, потому что у нее нет возможности узнать, может ли «а» быть изменено за ее спиной (даже константные переменные могут быть изменены).Например, один из способов «а» можно изменить без ведома компилятора, если у вас есть указатель, который указывает «а».Указатели не фиксируются во время выполнения, поэтому во время компиляции компилятор не может определить, будет ли указатель, который будет указывать и изменять «a».

0 голосов
/ 01 июля 2010

не совсем уверен, в чем смущение?

int a = 10; означает создать место в памяти и поместить значение 10 в адрес памяти

, если вы хотите, чтобы было 10

#define a 10

хотя чаще

#define TEN 10
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...