Я решил, что должен попытаться реализовать сопрограммы (я думаю, что я должен их назвать) для удовольствия и прибыли. Я ожидаю, что придется использовать ассемблер и, возможно, некоторые C, если я хочу сделать это полезным для всего.
Имейте в виду, что это для образовательных целей. Использование уже построенной библиотеки coroutine слишком просто (и действительно не весело).
Вы, ребята, знаете setjmp и longjmp? Они позволяют вам разматывать стек до предопределенного местоположения и возобновлять выполнение оттуда. Однако он не может перемотать "позже" в стек. Вернитесь раньше.
jmpbuf_t checkpoint;
int retval = setjmp(&checkpoint); // returns 0 the first time
/* lots of stuff, lots of calls, ... We're not even in the same frame anymore! */
longjmp(checkpoint, 0xcafebabe); // execution resumes where setjmp is, and now it returns 0xcafebabe instead of 0
То, что мне нужно - это способ запускать, без потоковой передачи, две функции на разных стеках. (Очевидно, только один запускается за один раз. Нет нити, я сказал.) Эти две функции должны иметь возможность возобновить другое исполнение (и остановить их собственное). Скорее всего, если они были друг с другом. Когда он вернется к другой функции, он должен вернуться туда, где он остался (то есть во время или после вызова, который дал управление другой функции), немного похоже на то, как longjmp возвращается к setjmp.
Вот как я это думал:
- Функция
Aсоздает и обнуляет параллельный стек (выделяет память и все такое). - Функция
Aперетаскивает все свои регистры в текущий стек. - Функция
Aустанавливает указатель стека и базовый указатель на это новое местоположение и вызывает таинственную структуру данных, указывающую, куда перепрыгивать назад и где указывать указатель команд назад. - Функция
Aобнуляет большинство своих регистров и устанавливает указатель на начало функцииB.
Это для инициализации. Теперь следующая ситуация будет бесконечно замкнутой:
- Функция
Bработает в этом стеке, выполняет любую работу, в которой она нуждается. - Функция
Bпереходит в точку, где требуется прервать и снова дать командуA. - Функция
Bподталкивает все свои регистры к своему стеку, берет таинственную структуру данныхAдала ее в самом начале и устанавливает указатель стека и указатель на инструкцию, где ему было указаноA. В этом процессе он возвращаетAновую модифицированную структуру данных, которая сообщает, где возобновитьB. - Функция
Aпросыпается, возвращая все регистры, которые она вставляет в свой стек, и работает до тех пор, пока не дойдет до точки, где ей необходимо прервать и снова дать командуB.
Все это звучит хорошо для меня. Тем не менее, есть ряд вещей, с которыми я не совсем согласен.
- По-видимому, на добром ol 'x86 была эта инструкция
pusha, которая отправила бы все регистры в стек. Однако архитектуры процессоров развиваются, и теперь с x86_64 у нас есть намного больше универсальных регистров и, вероятно, несколько регистров SSE. Я не мог найти никаких доказательств того, чтоpushaдействительно их подталкивает. В mordern x86 CPU имеется около 40 публичных регистров. Должен ли я сам делать всеpushes? Более того, для SSE-регистров нетpush(хотя он должен быть эквивалентным - я новый для всего этого "ассемблера x86" ). -
Меняет указатель на инструкцию так же просто, как сказать? Могу ли я сделать, например,Это простоmov rip, rax(синтаксис Intel)? Кроме того, получение значения от него должно быть несколько особенным, поскольку оно постоянно изменяется. Если мне больше нравитсяmov rax, rip(синтаксис Intel снова), будетripрасполагаться в командеmov, инструкции после него или где-то между?jmp foo. Пустышка. - Я несколько раз упоминал о таинственной структуре данных. До сих пор я предполагал, что он должен содержать как минимум три вещи: базовый указатель, указатель стека и указатель команд. Есть ли что-нибудь еще?
- Я что-то забыл?
- Пока я действительно хотел бы понять, как все работает, я уверен, что есть несколько библиотек, которые делают именно это. Вы знаете что-нибудь? Есть ли какой-либо стандартный способ для POSIX или BSD, например
pthreadдля потоков?
Спасибо, что прочитали мой <забавный > вопрос textwall.