system call : 자발적으로 현재 스래드가 실행할 필요 없다고 커널(os) 에게 알려주는 것

cout 같은걸 행했다면 커널모드에 요청을해야 처리가 가능해서 

이때 system call 을 요청하면 커널모드로 들어가게 되고 요청 받은걸 실행한 다음 다시 스레드를 재생하는 형태로 실행된다

 

 

스케줄러 그림인데

스레드 단위로 실행 될때 실행상태에서 자신이 다 실행했다고 하면 context switch 를 통해서 준비 단계로 이동 할 수 있다

그럼 나중에 cpu 가 스케줄러를 보고서 실행할 스레드를 결정하여 실행 처리를 한다

 

cout 은 자발적 문맥교환 이라 볼 수 있다

 

 

 

 

Sleep 함수 

 

유저 모드와 커널모드를 왔다갔다 할때

 

유저모드의 스레드들은 시간을 분할 받아 실행을 일정 주기 마다 하게 되는데 

 

sleep 함수는 커널모드에서 스케줄러에 의해 일정 시간 대기했다가 다시 실행시키는 함수이다

 

Sleep (system call)

From Wikipedia, the free encyclopedia
A computer program (process, task, or thread) may sleep, which places it into an inactive state for a period of time. Eventually the expiration of an interval timer, or the receipt of a signal or interrupt causes the program to resume execution.

 

#include "pch.h"
#include <iostream>
#include "CorePch.h"
#include <thread>	
#include <atomic>			//멀티 플랫폼에서 작동 가능
#include <vector>
#include <mutex>
#include "AccountManager.h"
#include "UserManager.h"
#include <chrono>

using namespace std;

mutex m1;
int32 sum = 0;

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

		//_locked 값이 기대 하는 값과 같으면 계속 무한 루프를 돌면서 spinlock 으로 계속 대기 하고, 그렇지 않고 expected 값이 locked 다르면
		//내부적으로 _locked 값을 desired 값으로 바꾼다음 while 을 탈출해 spinlock 에서 빠져나오게 한다
		while (_locked.compare_exchange_strong(expected, desired) == false)
		{
			
			//expected = false; 이렇게 설정해주면 locked 를 얻을 때까지 무한 대기 하게 되는데 cpu 를 사용하면서,
			expected = false;		//compare_exchange_strong 내부에서 expected 값을 & 로 바꾸기 때문에 원래 초기 상태로 바꾼다
			this_thread::sleep_for(100ms);		//100 ms 동안 sleep 하도록 함 => 이렇게 하면 cpu 파워를 낭비 하지 않고 context switch 된 이후에 일정 시간이후에 다시 와서 locked 를 획을 할 수 있을지 확인하게 되어
			//무한 cpu 자원을 사용하는건 막을 수 있지만 , 다시 돌아왔을때 이미 다른 스레드에서 lock 을 얻어 갔다면 일정 시간이후에 다시 돌아온 스레드는 또다시 wait 해야 하는상황이 발생되게 되는 악순환이 될 수도 있다
			//this_thread::yield();	// ==this_thread::sleep_for(0ms);  이것과 동일하다
			//이렇게 되면 엄밀히 말하면 spinlock 이 되는것은 아니다
			
		}
	}

	void unlock()
	{
		_locked.store(false);	//bool 을 바꾸는 것도 또한 원자적으로 일어나야함 
	}
	volatile atomic<bool>  _locked = false;	//bool 값도 그냥 바꾸면 명령어가 두개로 처리됨
};

SpinLock spinLock;

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

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

int main()
{

	thread t1(func1);
	thread t2(func2);

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

	std::cout << sum << std::endl;

	return 0;
}

 

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

     //expected = false; 이렇게 설정해주면 locked 를 얻을 때까지 무한 대기 하게 되는데 cpu 를 사용하면서,
     expected = false; //compare_exchange_strong 내부에서 expected 값을 & 로 바꾸기 때문에 원래 초기 상태로 바꾼다

 

this_thread::sleep_for(100ms);    100 ms 동안 sleep 하도록 함

 

이렇게 하면 cpu 파워를 낭비 하지 않고 context switch 된 이후에 일정 시간이후에 다시 와서

 

locked 를 획을 할 수 있을지 확인하게 되어
무한 cpu 자원을 사용하는건 막을 수 있지만 , 다시 돌아왔을때 이미 다른 스레드에서 lock 을 얻어 갔다면 일정 시간이후에 다시 돌아온 스레드는 또다시 wait 해야 하는상황이 발생되게 되는 악순환이 될 수도 있다

 

this_thread::yield();

위으 것과 this_thread::sleep_for(0ms);  이것과 동일하다

 

이렇게 되면 엄밀히 말하면 spinlock 이 되는것은 아니다

 

 

여기에서 포인트는 커널모드로 들어가서 Context Switch 를 유발하고 
유저모드에서 커널모드로 들어가게 할수 있다는 것임 

그리고 cout 같은 시스템 콜이 있다면 위와 같은 이유로 느려지게 된다는 것이다 

 

핵심은 system call 을 필요 없이 요청하는건 자제해야 한다는것!

 

 

 

 

 

ref : https://en.wikipedia.org/wiki/Sleep_(system_call)

 

 

반응형

+ Recent posts