์ˆ˜์—…


๐Ÿงต ์ฃผ์ œ

SpinLock ๊ฐœ์„ ์„ ์œ„ํ•œ Sleep ๋ฐ Yield ๊ธฐ๋ฐ˜ Lock ๊ตฌํ˜„๊ณผ OS ๋ ˆ๋ฒจ ์Šค์ผ€์ค„๋ง/๋ฌธ๋งฅ ๊ตํ™˜ ๊ตฌ์กฐ์˜ ์‹ค์ „ ์ดํ•ด

์ด ๊ธ€์€ ๊ธฐ์กด SpinLock ๋ฐฉ์‹์˜ ํ•œ๊ณ„๋ฅผ ๊ทน๋ณตํ•˜๊ธฐ ์œ„ํ•ด sleep_for, yield๋ฅผ ํ™œ์šฉํ•œ Lock ๋Œ€๊ธฐ ์ „๋žต์„ ์ œ์•ˆํ•œ๋‹ค. CPU ์ž์›์„ ๋‚ญ๋น„ํ•˜์ง€ ์•Š์œผ๋ฉด์„œ๋„ ์ปค๋„๊ณผ์˜ ํ˜‘์กฐ๋ฅผ ํ†ตํ•ด ํšจ์œจ์ ์œผ๋กœ ์žฌ์ง„์ž… ๊ฐ€๋Šฅํ•œ Lock์„ ๊ตฌํ˜„ํ•˜๋ฉฐ, ์ด ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•˜๋Š” system call, context switching, time slice ๊ฐœ๋…์„ OS ๊ด€์ ์—์„œ ์™„์ „ํ•˜๊ฒŒ ์„ค๋ช…ํ•œ๋‹ค.


๐Ÿ“˜ ๊ฐœ๋…

โœ… ๊ธฐ์กด SpinLock์˜ ํ•œ๊ณ„

  • while (_locked) ๋ฃจํ”„๋ฅผ ๊ณ„์† ๋Œ๋ฉฐ Lock์„ ์‹œ๋„ โ†’ ๋ฝ์„ ๋ชป ์–ป๋Š” ๋™์•ˆ์—๋„ CPU ์ ์œ 
  • ํŠนํžˆ ๋ฝ์ด ์˜ค๋ž˜ ์œ ์ง€๋  ๊ฒฝ์šฐ CPU ์ž์›์„ ์‹ฌ๊ฐํ•˜๊ฒŒ ๋‚ญ๋น„
  • ์„œ๋ฒ„ ๋“ฑ ๊ณ ์„ฑ๋Šฅ ์‹œ์Šคํ…œ์—์„œ๋Š” ์ด๋Ÿฐ ๋ฐฉ์‹์ด ๋ณ‘๋ชฉ์„ ์ดˆ๋ž˜ํ•  ์ˆ˜ ์žˆ์Œ

โœ… Sleep/ Yield ๊ธฐ๋ฐ˜ ์ „๋žต

  • ์‹คํŒจ ์‹œ ์ž ์‹œ ๊ธฐ๋‹ค๋ ธ๋‹ค๊ฐ€ ๋‹ค์‹œ Lock์„ ์‹œ๋„
  • ์ด ๊ธฐ๋‹ค๋ฆผ์€ sleep_for() ๋˜๋Š” yield()๋กœ ๊ตฌํ˜„๋˜๋ฉฐ ์‹คํ–‰๊ถŒ์„ ์ปค๋„์— ๋ฐ˜ํ™˜
  • ์ด ๊ณผ์ •์„ ํ†ตํ•ด CPU๋Š” ๋‹ค๋ฅธ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ์ด ๋ฐœ์ƒ

โœ… Time Slice๋ž€?

  • ์šด์˜์ฒด์ œ๊ฐ€ ์Šค๋ ˆ๋“œ์—๊ฒŒ "๋„ˆ ์ด๋งŒํผ ์‹œ๊ฐ„๋งŒ CPU ์จ!" ํ•˜๊ณ  ์ฃผ๋Š” ์‹คํ–‰ ์‹œ๊ฐ„ ์ฟ ํฐ
  • ์ด Time Slice๋ฅผ ๋‹ค ์“ฐ๊ฑฐ๋‚˜, sleep/yield ๋“ฑ์œผ๋กœ ์Šค์Šค๋กœ ํฌ๊ธฐํ•˜๋ฉด ์ปค๋„์ด ์‹คํ–‰๊ถŒ ํšŒ์ˆ˜

โœ… ์ž๋ฐœ์  ๋ฌธ๋งฅ ๊ตํ™˜ (Context Switching)

  • ์Šค๋ ˆ๋“œ๊ฐ€ ์Šค์Šค๋กœ ์‹คํ–‰์„ ๋ฉˆ์ถ”๊ณ  ์ปค๋„์— ์ œ์–ด๋ฅผ ๋„˜๊ธฐ๋Š” ํ–‰์œ„
  • sleep, yield, system call์€ ๋ชจ๋‘ ์ž๋ฐœ์  ๋ฌธ๋งฅ ๊ตํ™˜์„ ์œ ๋ฐœ
  • ์ปค๋„์€ ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋ฅผ ๊ณ ๋ฅด๊ณ , ํ˜„์žฌ ์Šค๋ ˆ๋“œ๋Š” ์ค€๋น„(ready) ์ƒํƒœ๋กœ ๋Œ€๊ธฐ

๐Ÿ“š ์šฉ์–ด ์ •๋ฆฌ

์šฉ์–ด์˜๋ฏธ
SpinLock๋ฝ์„ ์–ป์„ ๋•Œ๊นŒ์ง€ ๋ฌดํ•œ ๋ฃจํ”„ ๋Œ๋ฉฐ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋ฐฉ์‹
sleep_for(nms)์ง€์ •ํ•œ ์‹œ๊ฐ„ ๋™์•ˆ ์Šค๋ ˆ๋“œ๋ฅผ block ์ƒํƒœ๋กœ ๋งŒ๋“ค๊ณ  ์ปค๋„์— ์‹คํ–‰๊ถŒ์„ ๋„˜๊น€
yield()ํ˜„์žฌ ์Šค๋ ˆ๋“œ๊ฐ€ ์‹คํ–‰๊ถŒ์„ ๋ฐ˜๋‚ฉํ•˜๊ณ  ์ค€๋น„ ํ๋กœ ์ด๋™ (sleep_for(0ms)์™€ ๋™์ผ ํšจ๊ณผ)
Time SliceOS๊ฐ€ ์Šค๋ ˆ๋“œ์—๊ฒŒ ์ค€ CPU ์‚ฌ์šฉ ์‹œ๊ฐ„
Context Switching์‹คํ–‰ ์ค‘์ธ ์Šค๋ ˆ๋“œ๋ฅผ ์ค‘๋‹จํ•˜๊ณ  ๋‹ค๋ฅธ ์Šค๋ ˆ๋“œ๋กœ ์ „ํ™˜
System Call์œ ์ € ๋ชจ๋“œ์—์„œ ์ปค๋„ ๊ธฐ๋Šฅ ์š”์ฒญ ์‹œ ์‚ฌ์šฉํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค

๐Ÿ’ป ์ฝ”๋“œ ๋ถ„์„

โœ… ๊ฐœ์„ ๋œ SpinLock ๊ตฌํ˜„

class SpinLock
{
public:
	void lock()
	{
		bool expected = false;
		bool desired = true;

		while (_locked.compare_exchange_strong(expected, desired) == false)
		{
			expected = false;

			// ์ด ์Šค๋ ˆ๋“œ๋Š” ๋ฝ์„ ๋ชป ์–ป์—ˆ์œผ๋‹ˆ ์‹คํ–‰๊ถŒ์„ ์ปค๋„์—๊ฒŒ ๋ฐ˜๋‚ฉ
			this_thread::sleep_for(0ms);  // ๋˜๋Š” this_thread::yield();
		}
	}

	void unlock()
	{
		_locked.store(false); // ๋ฝ ํ•ด์ œ
	}

private:
	atomic<bool> _locked = false;
};

๐Ÿ” ์„ค๋ช…

  • compare_exchange_strong()๋Š” CAS(Compare-And-Swap) ์—ฐ์‚ฐ์œผ๋กœ Lock์„ ์–ป๋Š”๋‹ค
  • ์‹คํŒจ ์‹œ expected๋Š” ์ฐธ์กฐ๋กœ ์ „๋‹ฌ๋˜๊ธฐ ๋•Œ๋ฌธ์— false๋กœ ๋‹ค์‹œ ์ดˆ๊ธฐํ™”ํ•ด์•ผ ํ•œ๋‹ค
  • sleep_for(0ms) ๋˜๋Š” yield()๋ฅผ ํ†ตํ•ด ์Šค๋ ˆ๋“œ๋Š” ์ž๋ฐœ์ ์œผ๋กœ ์‹คํ–‰๊ถŒ์„ ์ปค๋„์—๊ฒŒ ๋„˜๊ธด๋‹ค
  • ์ด ๊ตฌ์กฐ๋Š” ๋ถˆํ•„์š”ํ•œ CPU ์ ์œ ๋ฅผ ํ”ผํ•  ์ˆ˜ ์žˆ๋Š” ๋งค์šฐ ์ค‘์š”ํ•œ ์ตœ์ ํ™”

โœ… ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

int32 sum = 0;
SpinLock spinLock;

void Add()
{
	for (int32 i = 0; i < 100000; i++)
	{
		lock_guard<SpinLock> guard(spinLock);
		sum++;
	}
}

void Sub()
{
	for (int32 i = 0; i < 100000; i++)
	{
		lock_guard<SpinLock> guard(spinLock);
		sum--;
	}
}

int main()
{
	thread t1(Add);
	thread t2(Sub);

	t1.join();
	t2.join();

	cout << sum << endl; // ํ•ญ์ƒ 0์ด ๋˜์–ด์•ผ ์ •ํ™•ํžˆ ๋™๊ธฐํ™”๋จ
}
  • lock_guard๋Š” lock()๊ณผ unlock()์„ ์ž๋™์œผ๋กœ ํ˜ธ์ถœํ•˜๋Š” RAII ๊ฐ์ฒด
  • SpinLock์ด ์ œ๋Œ€๋กœ ๋™์ž‘ํ–ˆ๋‹ค๋ฉด, ๋‘ ์Šค๋ ˆ๋“œ์˜ ํ•ฉ์‚ฐ/๊ฐ์‚ฐ์€ ์ •ํ™•ํžˆ 0์„ ๋ฐ˜ํ™˜

๐Ÿ“Š ๊ทธ๋ฆผ ๋ฐ ๋น„์œ  ํ•ด์„

๐Ÿœ ROOKISS์˜ ์‹๋‹น ๋น„์œ 

  • ์Šค๋ ˆ๋“œ = ์ง์›, ์ปค๋„ = ์‹๋‹น ๊ด€๋ฆฌ์ž, ์˜ํ˜ผ = CPU
  • Time Slice = ์ฟ ํฐ์ฒ˜๋Ÿผ ์ง€๊ธ‰
  • ์Šค๋ ˆ๋“œ๋Š” ์ฟ ํฐ(Time Slice)์„ ๋‹ค ์“ฐ๋ฉด ์ปค๋„์—๊ฒŒ ๋ฐ˜๋‚ฉํ•˜๊ณ  ๋‹ค์‹œ ๋Œ€๊ธฐ
  • sleep์€ ์ผ๋‹จ ์ž๋ฆฌ๋กœ ๋Œ์•„๊ฐ”๋‹ค๊ฐ€ ๋‹ค์‹œ ์˜ค๋Š” ์ „๋žต (๋žœ๋ค ๋ฉ”ํƒ€)

๐Ÿง  ์œ ์ €๋ชจ๋“œ โ†” ์ปค๋„๋ชจ๋“œ ์ „ํ™˜ ๊ตฌ์กฐ

  • cout, sleep, ์ž…์ถœ๋ ฅ ๋“ฑ์€ ๋ชจ๋‘ ์ปค๋„ ๋ฆฌ์†Œ์Šค ์š”์ฒญ โ†’ system call ๋ฐœ์ƒ
  • ์œ ์ € ์Šค๋ ˆ๋“œ โ†’ system call โ†’ ์ปค๋„ โ†’ block โ†’ ready โ†’ ์žฌ์‹คํ–‰ (upcall)
  • ์ด ๊ณผ์ •์€ ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ์ด๋ฉฐ, ๋ฐ˜๋“œ์‹œ ๋น„์šฉ์ด ์ˆ˜๋ฐ˜๋จ

๐Ÿšฆ ์Šค๋ ˆ๋“œ ์ƒํƒœ ์ „์ด ๋‹ค์ด์–ด๊ทธ๋žจ

  • ์ค€๋น„ โ†’ ์‹คํ–‰ โ†’ ๋Œ€๊ธฐ โ†’ ์ค€๋น„ โ†’ ์‹คํ–‰ ... ๋ฐ˜๋ณต
  • sleep์€ ๋Œ€๊ธฐ ์ƒํƒœ๋กœ ๋งŒ๋“ค๊ณ , ํƒ€์ด๋จธ๊ฐ€ ๋งŒ๋ฃŒ๋˜๋ฉด ์ค€๋น„ ์ƒํƒœ๋กœ ๋ณต๊ท€
  • yield๋Š” ํ˜„์žฌ ํƒ€์ž„ ์Šฌ๋ผ์ด์Šค๋ฅผ ๋ฐ˜๋‚ฉํ•˜๊ณ  ๋ฐ”๋กœ ์ค€๋น„ ์ƒํƒœ๋กœ ์ด๋™

๐ŸŽฏ ํ•ต์‹ฌ ์š”์•ฝ

  • SpinLock์˜ ๋ฌดํ•œ ๋ฃจํ”„๋Š” CPU ์ž์›์„ ๊ณผ๋„ํ•˜๊ฒŒ ์ ์œ  โ†’ Sleep์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐœ์„  ๊ฐ€๋Šฅ
  • sleep_for์™€ yield๋Š” ์ปค๋„ ์ง„์ž…์„ ์œ ๋„ํ•˜๊ณ  ์ž๋ฐœ์  ๋ฌธ๋งฅ ๊ตํ™˜์„ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค
  • sleep_for(0ms)์™€ yield()๋Š” ์˜๋ฏธ์ƒ ๊ฑฐ์˜ ๊ฐ™๋‹ค โ†’ ์‹คํ–‰๊ถŒ ํฌ๊ธฐ
  • compare_exchange_strong์—์„œ expected๋Š” ํ•ญ์ƒ ๋ฃจํ”„ ๋‚ด์—์„œ ์ดˆ๊ธฐํ™”ํ•ด์•ผ ์žฌ์‹œ๋„ ๊ฐ€๋Šฅ
  • Sleep ์‚ฌ์šฉ ์‹œ ์‹œ์Šคํ…œ ์ฝœ ๋ฐœ์ƒ โ†’ ์ปค๋„๋ชจ๋“œ ์ „ํ™˜ โ†’ context switching โ†’ ๋น„์šฉ ๋ฐœ์ƒ
  • ๋น ๋ฅด๊ฒŒ ๋Œ์•„์•ผ ํ•  ๋กœ์ง(์˜ˆ: ๊ฒŒ์ž„ ๋ฃจํ”„, ๋ Œ๋”๋ง, ์„œ๋ฒ„ ์ž…์ถœ๋ ฅ ๋“ฑ)์—๋Š” sleep, system call์„ ์ตœ์†Œํ™”ํ•ด์•ผ ํ•œ๋‹ค

profile
๏งกๅฎถ๋„ค_๊ณต๋ถ€๋ฐฉ

0๊ฐœ์˜ ๋Œ“๊ธ€