В xv6 есть файл spinlock.c для создания спин-блокировки для использования ядром. Но мне нужно реализовать API-интерфейсы спин-блокировки для использования на уровне пользователя. Например, я реализую sp_create() для создания спин-блокировки на уровне пользователя. Или вызовите sp_acquire(int id), чтобы получить блокировку, и т. д. Для этого я должен создать системные вызовы и поместить фактическую реализацию в ядро. xv6 имеет функцию спин-блокировки, но только для использования на уровне ядра.
Я думал о создании системных вызовов, которые на самом деле вызывают соответствующие функции в spinlock.c для создания блокировки, ее получения, освобождения и т. д. Но это не работает из-за некоторых проблем с отключением прерываний.
Я копирую ниже код, который я написал до сих пор:
//system call for lock_take():
int l_take(int lockid) {
struct proc *curproc = myproc();
//process will take lock
..
acquire(&LL.arraylockList[lockid].spnLock);
..
return 0;
}
Проблема, с которой я сталкиваюсь, заключается в том, что это дает мне ошибку о панике: запланированные блокировки, я думаю, это потому, что в коде accept() есть pushcli().
void
acquire(struct spinlock *lk)
{
pushcli(); // disable interrupts to avoid deadlock.
if (holding(lk)) panic("acquire");
// The xchg is atomic.
while (xchg(&lk->locked, 1) != 0)
;
// Tell the C compiler and the processor to not move loads or stores
// past this point, to ensure that the critical section's memory
// references happen after the lock is acquired.
__sync_synchronize();
// Record info about lock acquisition for debugging.
lk->cpu = mycpu();
getcallerpcs(&lk, lk->pcs);
}
Затем я скопировал код в новую функцию Acquis2() и использовал ее в своем системном вызове, где функция pushcli() закомментирована:
acquire2(struct spinlock *lk)
{
// pushcli(); // disable interrupts to avoid deadlock.
if (holding(lk)) panic("acquire");
// The xchg is atomic.
while (xchg(&lk->locked, 1) != 0) {
;
}
// Tell the C compiler and the processor to not move loads or stores
// past this point, to ensure that the critical section's memory
// references happen after the lock is acquired.
__sync_synchronize();
// Record info about lock acquisition for debugging.
lk->cpu = mycpu();
getcallerpcs(&lk, lk->pcs);
}
Однако затем сообщение об ошибке меняется на это: panic: mycpu() вызывается с включенными прерываниями.
Оказывается при отключении прерывания не допускаются. Таким образом, pushcli() и popcli() не следует использовать. Затем мне нужно выяснить, как запустить mycpu() атомарно. Его реализация такова:
// Must be called with interrupts disabled to avoid the caller being rescheduled
// between reading lapicid and running through the loop.
struct cpu *
mycpu(void)
{
int apicid, i;
if (readeflags() & FL_IF) panic("mycpu called with interrupts enabled\n");
apicid = lapicid();
// APIC IDs are not guaranteed to be contiguous. Maybe we should have
// a reverse map, or reserve a register to store &cpus[i].
for (i = 0; i < ncpu; ++i) {
if (cpus[i].apicid == apicid) return &cpus[i];
}
panic("unknown apicid\n");
}
Цикл for и строка над ним должны выполняться атомарно. Как мне это сделать?