Есть небольшие вопросы по двум системным функциям. Возможно, ответы на них следует отразить в документации.
f68.12 (mem_alloc)
Гарантируется ли обнуление выделенного блока памяти?
---------------------------------------
f77.2 (futex_wait)
Для входа в критическую секцию, поток должен выполнить два действия:
1) вызвать функцию f77.2
2) изменить значение контрольной переменной, чтобы закрыть вход для других потоков.
Может ли между первым и вторым действием произойти переключение задачи? Если да, то тогда перед вызовом f77.2 надо вызвать f68.1 (switch_task), чтобы получить новый квант времени, которого (вроде бы) гарантированно хватит для выполнения обоих действий. Но может быть функция f77.2 автоматически выделяет новый квант?
функции 68.12 и 77.2
Да.akron1 wrote:Гарантируется ли обнуление выделенного блока памяти?
Не понятен пункт 2. Фьютекс/мьютекс сам по себе критическая секция, если его правильно использовать. Это не маскирование прерываний или переключения задач. Если один поток получил блокировку, остальные будут ждать, пока фьютекс не разблокируют.akron1 wrote:f77.2 (futex_wait)
Для входа в критическую секцию, поток должен выполнить два действия:
1) вызвать функцию f77.2
2) изменить значение контрольной переменной, чтобы закрыть вход для других потоков.
На мой взгляд вызывать switch_task перед взятием фьютекса сомнительная затея. В ядре есть потоки с большим приоритетом, поэтому нельзя гарантировать, что поток получит полный квант даже после switch_task().akron1 wrote:Может ли между первым и вторым действием произойти переключение задачи? Если да, то тогда перед вызовом f77.2 надо вызвать f68.1 (switch_task), чтобы получить новый квант времени, которого (вроде бы) гарантированно хватит для выполнения обоих действий.
Но может быть функция f77.2 автоматически выделяет новый квант?
Я так использую. Может быть это в чем-то неправильно, но работает.Serge wrote: Не понятен пункт 2. Фьютекс/мьютекс сам по себе критическая секция, если его правильно использовать. Это не маскирование прерываний или переключения задач. Если один поток получил блокировку, остальные будут ждать, пока фьютекс не разблокируют.
Псевдокод:
Code: Select all
v = 0 //переменная -- контрольное значение фьютекса
futex = futex_create( adr(v) )
futex_wait(futex, 1, t) //если v == 1, поток ждет t сотых долей секунды, иначе выполнение продолжается
v = 1 //закрыть вход другим потокам
(* критическая секция *)
v = 0 //открыть вход (v != 1)
futex_wake(futex, 1) // разбудить не более одного потока
Code: Select all
futex_wait(futex, 1, t) //если v == 1, поток ждет t сотых долей секунды, иначе выполнение продолжается
v = 1 //закрыть вход другим потокам
Code: Select all
mov ecx, [ebp+FUTEX.pointer]
mov eax, edx
lock cmpxchg [ecx], edx
je .wait_slow
mov [esp+SYSCALL_STACK._eax], -2
ret
cmpxchg сравнивает EAX с [ecx]. если равно, устанавливает ZF и записывает edx в [ecx]. Иначе сбрасывает ZF и загружает [ecx] в eax.
В результате у тебя на входе v = [ecx] = 0; eax = edx = 1. cmpxchg сравнивает [ecx] = 0 c eax=1, сбрасывает zf и грузит [ecx] в eax. Значение v при этом не меняется.
Вот код из SDK
Code: Select all
static inline void mutex_lock(mutex_t *mutex)
{
int tmp;
if( __sync_fetch_and_add(&mutex->lock, 1) == 0)
return;
while (exchange_acquire (&mutex->lock, 2) != 0)
{
__asm__ volatile(
"int $0x40\t\n"
:"=a"(tmp)
:"a"(77),"b"(FUTEX_WAIT),
"c"(mutex->handle),"d"(2),"S"(0));
}
};
static inline void mutex_unlock(mutex_t *mutex)
{
int prev;
prev = exchange_release (&mutex->lock, 0);
if (prev != 1)
{
__asm__ volatile(
"int $0x40\t"
:"=a"(prev)
:"a"(77),"b"(FUTEX_WAKE),
"c"(mutex->handle),"d"(1));
};
};
futex - это системный вызов, который не стоит вызывать непосредственно никогда. Вообще никогда. Правильный способ его использования - найти нужный примитив синхронизации в какой-нибудь хорошо протестированной линуксовой библиотеке и взять его оттуда. Мьютекс из http://websvn.kolibrios.org/filedetails ... 2Fsync.inc , например, спёрт из glibc.
Сделаем мир лучше!
Всем спасибо, теперь мне всё понятно.
Who is online
Users browsing this forum: No registered users and 3 guests