spin lock

13660 단어 링크 ux 구동
응용 인터페이스 함수
spinlock_t my_lock;
unsigned long irqflags;
spin_lock_init(&my_lock);
spin_lock_irqsave(&my_lock, irqflags);
spin_unlock_irqrestore(&my_lock, irqflags);
상세 설명
1 데이터 구조
typedef struct spinlock {
	union {
		struct raw_spinlock rlock;

#ifdef CONFIG_DEBUG_LOCK_ALLOC
# define LOCK_PADSIZE (offsetof(struct raw_spinlock, dep_map))
		struct {
			u8 __padding[LOCK_PADSIZE];
			struct lockdep_map dep_map;
		};
#endif
	};
} spinlock_t;
typedef struct raw_spinlock {
	arch_spinlock_t raw_lock;
#ifdef CONFIG_GENERIC_LOCKBREAK
	unsigned int break_lock;
#endif
#ifdef CONFIG_DEBUG_SPINLOCK
	unsigned int magic, owner_cpu;
	void *owner;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map dep_map;
#endif
} raw_spinlock_t;
typedef struct {
	union {
		u32 slock;
		struct __raw_tickets {
#ifdef __ARMEB__
			u16 next;
			u16 owner;
#else
			u16 owner;
			u16 next;
#endif
		} tickets;
	};
} arch_spinlock_t;

arch_spinlock_t 는 구조 와 관련 된 type 으로 slock 과 를 포함한다.raw_티켓 두 멤버.ARMEB__Big - Endian 을 대표 합 니 다. 이렇게 slock 은 두 부분 으로 나 뉘 어 집 니 다.
                                                u32 slock
-----------------------------------------------------------------------------------
|           u16 next                         |                   u16 owner              | 
 -----------------------------------------------------------------------------------
bit31                                      bit16 bit15                                bit0
2 spin_lock_init()
#define spin_lock_init(_lock)				\
do {							\
	spinlock_check(_lock);				\
	raw_spin_lock_init(&(_lock)->rlock);		\
} while (0)
static inline raw_spinlock_t *spinlock_check(spinlock_t *lock)
{
	return &lock->rlock;
}
spinlock_check rlock 구성원 변 수 를 통 해 인자 가 spinlock 인지 확인 합 니 다.t 형식, 컴 파일 과 실행 이 아 닐 때 warnning 이나 error 가 있 습 니 다.
# define raw_spin_lock_init(lock)				\
	do { *(lock) = __RAW_SPIN_LOCK_UNLOCKED(lock); } while (0)
#define __RAW_SPIN_LOCK_UNLOCKED(lockname)	\
	(raw_spinlock_t) __RAW_SPIN_LOCK_INITIALIZER(lockname)
#define __RAW_SPIN_LOCK_INITIALIZER(lockname)	\
	{					\
	.raw_lock = __ARCH_SPIN_LOCK_UNLOCKED,	\
	SPIN_DEBUG_INIT(lockname)		\
	SPIN_DEP_MAP_INIT(lockname) }

raw 때문에lock = 0, 그래서 slock = tickets. next = tickets. owner = 0.
#define __ARCH_SPIN_LOCK_UNLOCKED	{ { 0 } }

3 spin_lock_irqsave()
typedef struct {
	union {
		u32 slock;
		struct __raw_tickets {
#ifdef __ARMEB__
			u16 next;
			u16 owner;
#else
			u16 owner;
			u16 next;
#endif
		} tickets;
	};
} arch_spinlock_t;
#define spin_lock_irqsave(lock, flags)				\
do {								\
	raw_spin_lock_irqsave(spinlock_check(lock), flags);	\
} while (0)
#define raw_spin_lock_irqsave(lock, flags)			\
	do {						\
		typecheck(unsigned long, flags);	\
		flags = _raw_spin_lock_irqsave(lock);	\
	} while (0)
unsigned long __lockfunc _raw_spin_lock_irqsave(raw_spinlock_t *lock)
{
	return __raw_spin_lock_irqsave(lock);
}
static inline unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock)
{
	unsigned long flags;

	local_irq_save(flags);//    
	preempt_disable();
/*        ,            lock    ,CPU    ,          ,  ,        spin_lock,  spin_lock    sleep,        。             。*/
	spin_acquire(&lock->dep_map, 0, 0, _RET_IP_);
	/*
	 * On lockdep we dont want the hand-coded irq-enable of
	 * do_raw_spin_lock_flags() code, because lockdep assumes
	 * that interrupts are not re-enabled during lock-acquire:
	 */
#ifdef CONFIG_LOCKDEP
	LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
#else
	do_raw_spin_lock_flags(lock, &flags);
#endif
	return flags;
}

spin_acquire () 는 debug 를 제공 할 때 사용 되 며, 정상 적 인 경우 에는 비어 있 습 니 다.
#ifdef CONFIG_DEBUG_LOCK_ALLOC
# ifdef CONFIG_PROVE_LOCKING
#  define spin_acquire(l, s, t, i)		lock_acquire(l, s, t, 0, 2, NULL, i)
#  define spin_acquire_nest(l, s, t, n, i)	lock_acquire(l, s, t, 0, 2, n, i)
# else
#  define spin_acquire(l, s, t, i)		lock_acquire(l, s, t, 0, 1, NULL, i)
#  define spin_acquire_nest(l, s, t, n, i)	lock_acquire(l, s, t, 0, 1, NULL, i)
# endif
# define spin_release(l, n, i)			lock_release(l, n, i)
#else
# define spin_acquire(l, s, t, i)		do { } while (0)
# define spin_release(l, n, i)			do { } while (0)
#endif

3.1 spin_trylock
#ifdef CONFIG_LOCK_STAT

extern void lock_contended(struct lockdep_map *lock, unsigned long ip);
extern void lock_acquired(struct lockdep_map *lock, unsigned long ip);
LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);
#define LOCK_CONTENDED(_lock, try, lock)			\
do {								\
	if (!try(_lock)) {					\
		lock_contended(&(_lock)->dep_map, _RET_IP_);	\
		lock(_lock);					\
	}							\
	lock_acquired(&(_lock)->dep_map, _RET_IP_);			\
} while (0)
/*   do_raw_spin_trylock()    lock。      lock,    do_raw_spin_lock()  lock,   sleep。
*/
#else /* CONFIG_LOCK_STAT */

#define lock_contended(lockdep_map, ip) do {} while (0)
#define lock_acquired(lockdep_map, ip) do {} while (0)

#define LOCK_CONTENDED(_lock, try, lock) \
	lock(_lock)

#endif /* CONFIG_LOCK_STAT */
static inline int do_raw_spin_trylock(raw_spinlock_t *lock)
{
	return arch_spin_trylock(&(lock)->raw_lock);
}
%0->slock %1->tmp %2->&lock->slock %3->1 << TICKET_SHIFT(1 << 16)
slock = lock->slock;
tmp = slock - cpu_to_le32;(slock) / or 는 순환 오른쪽으로 이동 합 니 다. 자 물 쇠 는 tmp = 0 을 사용 할 수 있 습 니 다. 그렇지 않 으 면 tmp = 1 을 사용 할 수 있 습 니 다.
if (tmp == 0) slock = slock + (1 << 16);//slock. tickets. next + 1 에 해당 합 니 다.
strex 와 ldrex 는 하나의 명령 으로 ARM core 버 전 > = 6 만 있 습 니 다.ldrex 에서 strex 사이 에 다른 CPU 나 DMA 가 있 는 지 버스 에서 감시 할 수 있 습 니 다. 있 으 면 strex 는 첫 번 째 레지스터 에 값 을 1 (non - exclusive by this CPU) 로 설정 하고 store 동작 을 실패 하 게 합 니 다. 없 으 면 strex 는 첫 번 째 레지스터 에 0 (exclusive access by this CPU) 으로 설정 하고 store 동작 을 성공 적 으로 합 니 다.
if (tmp == 0) lock->slock = slock; tmp = 0; strex 는 원자의 조작 을 보증 합 니 다. 만약 check 에서 자 물 쇠 를 사용 할 수 있다 면 tmp = 0 을 잠 그 십시오.잠 금 에 실패 하면 tmp = 1.
arch_spin_trylock () 함 수 는 원자 조작 입 니 다. lock 이 잠 겨 있 는 지, 잠 겨 있 지 않 으 면 잠 겨 있 는 지 테스트 합 니 다.돌아 가기 1;그렇지 않 으 면 0 으로 돌아 갑 니 다.
3.2 spin_lock
static inline int arch_spin_trylock(arch_spinlock_t *lock)
{
	unsigned long tmp;
	u32 slock;

	__asm__ __volatile__(
"	ldrex	%0, [%2]
" //%0 0 , " subs %1, %0, %0, ror #16
" " addeq %0, %0, %3
" " strexeq %1, %0, [%2]" : "=&r" (slock), "=&r" (tmp) // ,“=” : "r" (&lock->slock), "I" (1 << TICKET_SHIFT) : "cc"); if (tmp == 0) { smp_mb(); return 1; } else { return 0; } }
void do_raw_spin_lock(raw_spinlock_t *lock)
{
	debug_spin_lock_before(lock);
	if (unlikely(!arch_spin_trylock(&lock->raw_lock)))
		__spin_lock_debug(lock);
	debug_spin_lock_after(lock);
}
static inline void do_raw_spin_lock(raw_spinlock_t *lock) __acquires(lock)
{
	__acquire(lock);
	arch_spin_lock(&lock->raw_lock);
}
%0->lockval %1->newval %2->tmp %3->&lock->slock %4->(1 << TICKET_SHIFT)
lockval = &lock->slock;
newval = lockval + (1 << TICKET_SHIFT);//lockval. tickets. next + 1 에 해당 합 니 다.
lock->slock = newval;//tmp 의 값 은 이 원칙 작업 의 성공 여부 에 달 려 있 습 니 다. 성공 은 0 입 니 다.
if (tmp! = 0) 첫 번 째 tag (레이 블 1) 로 이동, goto 1;//store
실패 하면 쉬 지 않 고 뛰 어 돌아간다.
레이 블 f: 앞으로 뛰 고 순서대로 실행 하면 실행 되 지 않 은 프로그램, front 라 는 뜻 입 니 다.
레이 블 b: 이전에 실 행 했 던 문장 으로 넘 어 가 는 것 을 의미 합 니 다. 첫 번 째 1 레이 블 에서 back 이라는 뜻 입 니 다.
if (lockval. tickets. next! = lockval. tickets. owner) 는 자물쇠 가 잠 겨 있 으 면 wfe 명령 을 실행 합 니 다.wfe 명령 을 통 해 suspend mode (clock 정지) 에 들 어가 이 자물쇠 가 풀 릴 때 보 내 는 sev 명령 을 받 아야 CPU 가 suspend mode 에서 벗 어 나 if (lockval. tickets. next! = lockval. tickets. owner) 를 확인 하고 계속 기다 릴 수 있 습 니 다.
4 spin_unlock_irqrestore()
static inline void arch_spin_lock(arch_spinlock_t *lock)
{
	unsigned long tmp;
	u32 newval;
	arch_spinlock_t lockval;

	__asm__ __volatile__(
"1:	ldrex	%0, [%3]
" " add %1, %0, %4
" " strex %2, %1, [%3]
" " teq %2, #0
" // cpsr->Z " bne 1b" : "=&r" (lockval), "=&r" (newval), "=&r" (tmp) : "r" (&lock->slock), "I" (1 << TICKET_SHIFT) : "cc"); while (lockval.tickets.next != lockval.tickets.owner) { wfe(); lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner); } smp_mb(); }
static inline void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)
{
	raw_spin_unlock_irqrestore(&lock->rlock, flags);
}
#define raw_spin_unlock_irqrestore(lock, flags)		\
	do {							\
		typecheck(unsigned long, flags);		\
		_raw_spin_unlock_irqrestore(lock, flags);	\
	} while (0)
void __lockfunc _raw_spin_unlock_irqrestore(raw_spinlock_t *lock, unsigned long flags)
{
	__raw_spin_unlock_irqrestore(lock, flags);
}
static inline void __raw_spin_unlock_irqrestore(raw_spinlock_t *lock,
					    unsigned long flags)
{
	spin_release(&lock->dep_map, 1, _RET_IP_);
	do_raw_spin_unlock(lock);
	local_irq_restore(flags);//    
	preempt_enable();//    
}
static inline void do_raw_spin_unlock(raw_spinlock_t *lock) __releases(lock)
{
	arch_spin_unlock(&lock->raw_lock);
	__release(lock);
}
static inline void arch_spin_unlock(arch_spinlock_t *lock)
{
	smp_mb();
	lock->tickets.owner++;
	dsb_sev();
}

armv 7 이상 의 구조 에 대해 dsb 명령 을 호출 하여 실행 이 끝 난 후에 야 프로그램 에서 이 명령 뒤에 있 는 명령 을 실행 할 수 있 습 니 다.
다른 task 가 있 으 면 spin 을 다시 호출 할 수 있 음 을 알 수 있 습 니 다.lock, lock 의 lockval. tickets. next + 때문에
그래서 lockval. tickets. next! =lockval. tickets. owner 는 줄곧 loop 에 있 습 니 다.자 물 쇠 를 든 task 가 spin 을 호출 할 때 까지unlock, lockval. tickets. owner + 를 사용 해 야 loop 에서 벗 어 날 수 있 습 니 다.
이 메커니즘 은 리 눅 스 - 2.6.25 에 커 널 을 넣 고 리 눅 스 - 3.6 에 ARM 구 조 를 넣 은 것 이다. 즉, 다음 에 소 개 된 것 은 유명한 Ticket spinlocks 가 줄 을 서서 자 물 쇠 를 돌 리 는 것 이다.
3 티켓 스 핀 락
전통 적 인 자 회전 자물쇠 의 '불공평' 문 제 는 자 물 쇠 를 잠 그 는 경쟁 이 치열 한 서버 시스템 에서 특히 심각 하기 때문에 리 눅 스 커 널 개발 자 닉 피 긴 은 리 눅 스 커 널 2.6.25 버 전에 서 줄 서기 자 물 쇠 를 도입 했다. 실행 스 레 드 가 자 물 쇠 를 신청 하 는 순서 정 보 를 저장 함으로써 '불공평' 문 제 를 해결 했다.
줄 서기 자전 자 물 쇠 는 여전히 원래 의 raw 를 사용 합 니 다.spinlock_t 데이터 구 조 는 slock 역 에 새로운 의 미 를 부여 합 니 다.순서 정 보 를 저장 하기 위해 slock 도 메 인 은 두 부분 으로 나 뉘 어 잠 금 소지 자 와 미래 잠 금 신청자 의 대기 번호 (Ticket Number) 를 각각 저장 합 니 다. 다음 과 같 습 니 다.
                                                u32 slock
-----------------------------------------------------------------------------------
|           u16 next                         |                   u16 owner              | 
 -----------------------------------------------------------------------------------
bit31                                      bit16 bit15                                bit0
owner 와 next 도 메 인 은 모두 16 비트 이 며, 그 중에서 owner 도 메 인 은 slock 의 낮은 16 비트 입 니 다.대기 열 자동 잠 금 은 최대 2 ^ 16 = 65536 개의 프로세서 가 지원 되 는 것 을 알 수 있 습 니 다.next 도 메 인 이 owner 도 메 인 과 같 을 때 만 잠 금 이 사용 되 지 않 은 상태 임 을 나타 낸다 (이때 도 이 잠 금 을 신청 하 는 사람 이 없다).줄 서기 자동 잠 금 초기 화 시 slock 은 0 으로 설정 되 어 있 습 니 다. 즉, owner 와 next 는 0 으로 설정 되 어 있 습 니 다.커 널 실행 스 레 드 가 자전 자 물 쇠 를 신청 할 때 원 자 는 next 도 메 인 에 1 을 추가 하고 원 next 값 을 자신의 대기 번호 로 합 니 다.이 대기 번호 가 신청 시의 owner 값 과 같 으 면 자 회전 자물쇠 가 사용 되 지 않 은 상태 에 있 음 을 설명 하고 자 물 쇠 를 직접 획득 합 니 다.그렇지 않 으 면 이 스 레 드 는 owner 필드 가 자신 이 가지 고 있 는 대기 번호 와 같 는 지 확인 하 느 라 바 쁘 고, 같 으 면 자 물 쇠 를 가 져 올 차례 임 을 나타 낸다.스 레 드 가 잠 금 을 풀 때 원자 지 는 owner 도 메 인 을 1 로 추가 하면 됩 니 다. 다음 스 레 드 는 이 변 화 를 발견 하고 바 쁜 대기 상태 에서 종료 합 니 다.스 레 드 는 신청 순서에 따라 줄 을 서서 자 물 쇠 를 엄 격 히 가 져 와 '불공평' 문 제 를 완전히 해결 할 것 이다.

좋은 웹페이지 즐겨찾기