Каков самый быстрый способ перевести все 64-разрядные регистры в стек? [Дубликат]

Вместо того, чтобы бросать код на вас, есть два понятия, которые являются ключом к пониманию того, как JS обрабатывает обратные вызовы и асинхронность. (это даже слово?)

Модель цикла события и параллелизма

Есть три вещи, о которых вам нужно знать; Очередь; цикл события и стек

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

while (queue.waitForMessage()) {
   queue.processNextMessage();
}

Как только он получает сообщение для запуска чего-то, он добавляет его в очередь. Очередь - это список вещей, которые ждут выполнения (например, ваш запрос AJAX). Представьте себе это так:

 1. call foo.com/api/bar using foobarFunc
 2. Go perform an infinite loop
 ... and so on

Когда одно из этих сообщений будет исполнено, оно выталкивает сообщение из очереди и создает стек, стек - это все, что нужно выполнить JS для выполнения инструкции в сообщение. Таким образом, в нашем примере ему говорят позвонить foobarFunc

function foobarFunc (var) {
  console.log(anotherFunction(var));
}

. Так что все, что foobarFunc должно выполнить (в нашем случае anotherFunction), будет вставлено в стек. исполняемый, а затем забытый - цикл события затем переместится на следующую вещь в очереди (или прослушивает сообщения)

. Главное здесь - порядок выполнения. Это

КОГДА что-то будет запущено

Когда вы совершаете вызов с использованием AJAX для внешней стороны или выполняете любой асинхронный код (например, setTimeout), Javascript зависит от ответ, прежде чем он сможет продолжить.

Большой вопрос, когда он получит ответ? Ответ в том, что мы не знаем, поэтому цикл событий ждет, когда это сообщение скажет: «Эй, забери меня». Если JS просто ждал этого сообщения синхронно, ваше приложение замерзнет, ​​и оно сосать. Таким образом, JS продолжает выполнение следующего элемента в очереди, ожидая, пока сообщение не будет добавлено обратно в очередь.

Вот почему с асинхронной функциональностью мы используем вещи, называемые обратными вызовами. Это похоже на обещание буквально. Как и в I , обещание что-то вернуть в какой-то момент jQuery использует специальные обратные вызовы, называемые deffered.done deffered.fail и deffered.always (среди других). Вы можете увидеть их все здесь

Итак, вам нужно передать функцию, которая в какой-то момент будет выполнена с переданными ей данными.

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

function foo(bla) {
  console.log(bla)
}

, поэтому большую часть времени (но не всегда) вы пройдете foo не foo()

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

21
задан Ciro Santilli 新疆改造中心 六四事件 法轮功 22 March 2015 в 15:35
поделиться

4 ответа

Изучите существующий код, который делает подобные вещи. Например:

Фактически, «ручное нажатие» рег является единственным способом на AMD64, так как PUSHA там не существует. AMD64 не уникален в этом аспекте - большинство процессоров, отличных от x86, требуют некоторого сохранения / восстановления регистров по регистру.

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

7
ответ дан alanc 28 August 2018 в 18:15
поделиться

Привет, возможно, это не правильный способ сделать это, но можно создать макросы, такие как

.macro pushaq
    push %rax
    push %rcx
    push %rdx
    push %rbx
    push %rbp
    push %rsi
    push %rdi
.endm # pushaq

и

.macro popaq
    pop %rdi    
    pop %rsi    
    pop %rbp    
    pop %rbx    
    pop %rdx    
    pop %rcx
    pop %rax
.endm # popaq

, и в итоге добавить другие регистры r8-15 если нужно

0
ответ дан baz 28 August 2018 в 18:15
поделиться

AMD нуждалась в некоторой возможности для добавления новых кодов операций для префиксов REX и некоторых других новых инструкций при разработке 64-разрядных расширений x86. Они изменили значение некоторых кодов операций на эти новые инструкции.

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

Порядок из PUSHA был порядок кодирования команд: eax, ecx, edx, ebx, esp, ebp, esi, edi. Обратите внимание, что он избыточно нажал esp! Вам нужно знать esp, чтобы найти данные, которые он нажал!

Если вы конвертируете код из 64-битного кода PUSHA, все равно, вам нужно его обновить, чтобы нажать новые регистры r8 через r15. Вам также необходимо сохранить и восстановить гораздо большее состояние SSE, xmm8 через xmm15. Предполагая, что вы собираетесь их сбивать.

Если код обработчика прерываний - это просто заглушка, которая пересылает код C, вам не нужно сохранять все регистры. Вы можете предположить, что компилятор C будет генерировать код, который будет сохранять rbx, rbp, rsi, rdi и r12 через r15. Вам нужно сохранить и восстановить rax, rcx, rdx и r8 через r11. (Примечание: на Linux или других платформах System V ABI компилятор будет сохранять rbx, rbp, r12 - r15, вы можете ожидать rsi и rdi clobbered) .

Регистры сегментов не сохраняют значения в длинном режиме (если прерываемый поток работает в режиме совместимости с 32 битами, вы должны сохранять регистры сегментов, спасибо ughoavgfhw). Фактически, они избавились от большей части сегментации в длинном режиме, но FS по-прежнему зарезервирован для использования операционными системами в качестве базового адреса для локальных данных потока. Само значение регистра не имеет значения, база FS и GS устанавливается через MSR 0xC0000100 и 0xC0000101. Предполагая, что вы не будете использовать FS, вам не нужно беспокоиться об этом, просто помните, что любые локальные данные потока, к которым обращается код C, могут использовать TLS любого случайного потока. Будьте осторожны, потому что библиотеки времени выполнения C используют TLS для некоторой функциональности (например: strtok обычно использует TLS).

Загрузка значения в FS или GS (даже в пользовательском режиме) перезапишет FSBASE или GSBASE MSR. Поскольку некоторые операционные системы используют GS как «локальное» процессорное хранилище (им нужен способ иметь указатель на структуру для каждого ЦП), им необходимо сохранить его где-то, что не будет сбиваться при загрузке GS у пользователя Режим. Для решения этой проблемы существуют два MSR, зарезервированных для регистра GSBASE: один активный и один скрытый. В режиме ядра ядро ​​GSBASE хранится в обычном GSBASE MSR, а база пользовательского режима находится в другом (скрытом) GSBASE MSR. Когда контекст переключается из режима ядра в контекст пользовательского режима, а при сохранении контекста пользовательского режима и входа в режим ядра, код переключения контекста должен выполнять команду SWAPGS, которая меняет значения видимого и скрытого GSBASE MSR. Поскольку ядро ​​GSBASE безопасно скрыто в другом режиме MSR в пользовательском режиме, код режима пользователя не может сжимать GSBASE ядра, загружая значение в GS. Когда CPU снова загрузит режим ядра, код сохранения контекста выполнит SWAPGS и восстановит GSBASE ядра.

15
ответ дан doug65536 28 August 2018 в 18:15
поделиться

pusha недопустим в 64-битном режиме, поскольку он является избыточным. [1] [1]

4
ответ дан user200783 28 August 2018 в 18:15
поделиться
Другие вопросы по тегам:

Похожие вопросы: