Механизм публикации-подписки и критический раздел на стороне чтения в блокировке RCU
Вопрос 1:
В статье о введении блокировки RCU он пишет механизм публикации-подписки. Но у меня есть вопрос о rcu_assign_pointer(), в этой статье он сказал:
1 p->a = 1;
2 p->b = 2;
3 p->c = 3;
4 rcu_assign_pointer(gp, p);
Rcu_assign_pointer() опубликует новую структуру, вынуждая и компилятор, и ЦП выполнить присваивание gp после присваиваний полям, на которые ссылается p.
Но как мог компилятор и процессор знать, что p назначен? Например, если я просто инициализирую p-> a и p-> b, то как компилятор и CPU различают две ситуации?
situation 1:
1 p->a = 1;
2 p->b = 2;
3 p->c = 3;
4 rcu_assign_pointer(gp, p);
situation 2:
1 p->a = 1;
2 p->b = 2;
3 rcu_assign_pointer(gp, p);
Вопрос 2:
Что касается критического раздела на стороне чтения, если для чтения данных имеются непрерывные считыватели, должен ли писатель ждать их, или писатель не может выполнить операцию синхронизации? Если да, читатель всегда будет читать старую версию?
1 ответ
Ответ на вопрос 1: rcu_assign_pointer() дает указание компилятору не оптимизировать машинные инструкции, так что любые модификации полей 'p', сделанные до оператора rcu_assign_pointer () в реализации / коде, выполняются в конвейере CPU до присвоение "р" для "gp". Это гарантирует, что при назначении "gp" все поля "p" и, следовательно, теперь "gp", которые разработчик решил назначить, действительно назначаются.
Чтобы проиллюстрировать это далее, возьмем следующий код, цель которого состоит в том, чтобы вернуть 'y' обратно вызывающей стороне:
void
my_func(struct foo **y)
{
struct foo *x = malloc(sizeof (struct foo));
x->val1 = 1;
*y = x;
x->val2 = 2;
return;
}
Разработчик на самом деле не заботится о порядке приведенных выше операторов присваивания, потому что он / она намеревается просто вернуть *y, заполненный двумя значениями. Следовательно, с учетом кода, подобного приведенному выше, компилятор может выбрать выполнение 3 операторов присваивания параллельно в конвейерах ЦП, и это не нарушает корректность.
Теперь, если вы используете аналогичный оператор присваивания при выполнении работы типа rcu, компилятор может решить выполнить тот же тип оптимизации, и, следовательно, читатели могут получить "частично инициализированный" "gp". rcu_assign_pointer() гарантирует, что порядок назначения поддерживается таким образом, что намерение разработчика назначить 'p' для 'gp' ПОСЛЕ того, как все поля p были инициализированы, сохранено.
Надеюсь, это должно проиллюстрировать, что если вы, разработчик, решили назначить только p->a и p->b, не назначая p->c, перед тем, как назначить 'p' для 'gp', то это то, что получит gp - a частично инициализированная структура.
Ответ на вопрос2: