2009-08-16 2 views
4

What happens when you run a program?과 유사하지만 속지는 아닙니다.하나의 메소드가 다른 메소드를 호출 할 때 후드에서 어떤 일이 발생합니까?

이의 내가

public static void RunSnippet() 
    { 
     TestClass t = new TestClass(); 
     t.A(1, 2); 

     t.B(3, 4); 
    } 

    public class TestClass 
    { 
     public void A(int param1, int param2) 
     { 
      //do something 
      C(); 
     } 

     private void C() 
     { 
      //do 
     } 

     public bool B(int param1, int param2) 
     { 
      //do something 
      bool result = true; 

      return result; 
     } 
    } 

누군가가 자세히 설명 할 수 (그러나 간단한 일반 영어 보관하십시오) 두 가지 방법 AAND B. 간단한 콘솔 프로그램이 있다고 가정 해 봅시다, 정말로 일어나는 것은 RunSnippet는 방법 A를 호출 할 때와 메소드 B (내부적으로 다른 메소드를 호출). 후드에서 실제로 일어나는 일을 이해하고 싶습니다 ... 매개 변수 전달 방법, 저장 위치, 로컬 변수, 리턴 값 전달 방법 A가 C를 호출 할 때 다른 스레드가 실행되면 어떻게 될까요? 예외가 발생하면 어떻게됩니까?

+0

당신이 말한 질문과 같이, 이것은 너무 광범위한 질문입니다. 전체 챕터 (또는 전체 책)이 주제에 기록되었습니다. –

+0

@ Neeil이 동의했지만, 아주 간단한 예를 들자면, 괜찮은 개요가 설명 될 수 있다고 생각합니다. 내 시도를 아래에서보십시오. –

답변

10

난 당신이 찾고있는 세부 사항의 수준을 아주 잘 모르겠지만, 여기 내 찌르고 무슨 일이 일어나고 있는지 설명에서이다 :

  1. 새로운 프로세스가 실행 파일이 생성됩니다. 이 프로세스에는 각 스레드의 스택을 포함하는 스택 세그먼트, 정적 변수에 대한 데이터 세그먼트 및 동적으로 할당 된 메모리에 대한 힙이라는 메모리 블록과 컴파일 된 코드가 포함 된 코드 세그먼트가 있습니다.
  2. 코드가 코드 세그먼트에로드되고, 명령 포인터가 main() 메서드의 첫 번째 명령으로 설정되고 코드가 실행을 시작합니다.
  3. 오브젝트 t는 힙에서 할당됩니다. t의 주소는 스택에 저장됩니다 (각 스레드에는 스택이 있습니다).
  4. t.A()는 반환 주소를 스택의 main()에 배치하고 명령 포인터를 t.A()의 코드 시작으로 변경하여 호출됩니다. 반환 주소는 값 1과 2와 함께 스택에 배치됩니다.
  5. tA()는 스택에 반환 주소를 tA()로 설정하고 명령 포인터를 tC의 시작 주소로 변경하여 tC()를 호출합니다)의 코드.
  6. t.C()는 리턴 주소를 스택의 t.A()에서 popping하여 반환하고 명령 포인터를 그 값으로 설정합니다.
  7. t.A()는 t.C()와 비슷한 방식으로 반환됩니다.
  8. t.B()에 대한 호출은 값을 반환한다는 점을 제외하고는 t.A()에 대한 호출과 매우 유사합니다. 이 값을 반환하는 정확한 메커니즘은 언어 및 플랫폼에 따라 다릅니다. 종종 값은 CPU 레지스터에 반환됩니다.

참고 : 방법이 매우 작기 때문에 최신 컴파일러는 클래식 호출 대신 "인라인"하는 경우가 많습니다. 인라이닝이란 메서드에서 코드를 가져 와서 함수 호출을 수행하는 오버 헤드를 거치지 않고 main() 메서드에 코드를 직접 주입하는 것을 의미합니다.

예를 들어 볼 때 스레딩이 어떻게 사진에 직접 나타날 수 있는지 보지 못했습니다. 실행 파일을 다시 시작하려면 새 프로세스에서 실행해야합니다. 즉, 자체 코드 세그먼트, 데이터 세그먼트 및 스택 세그먼트가 첫 번째 프로세스와 완전히 분리되어 있음을 의미합니다.

코드가 여러 스레드에서 main()이라는 더 큰 프로그램 내에서 실행되면 이전에 설명한대로 거의 실행됩니다. 이 코드는 정적 변수와 같은 잠재적으로 공유 된 리소스에 액세스하지 않기 때문에 스레드로부터 안전합니다. 모든 핵심 데이터 (값 및 객체에 대한 포인터)가 스레드의 로컬 스택에 저장되므로 스레드 1이 스레드 2를 "볼"수있는 방법은 없습니다.

+0

+1이 모양이 좋습니다. 감사. – Sandbox

+0

스레딩과 관련된 문제는 내 사례와 정확히 관련이 없습니다. 하지만, 예를 들어 작업 스레드에서 실행 중이며 다른 스레드가 주 스레드에 의해 생성 된 경우 현재 실행중인 메서드는 어떻게됩니까? – Sandbox

+1

main이 다른 스레드를 생성하면 최종 "}"로 실행을 계속합니다. 그 시점에서 결과는 더 이상 언어 및 플랫폼에 영향을받지 않으며 (즉, 플랫폼 및 언어에 따라 조금씩 다르지만) 일반적으로 기본 메소드가 종료되면 프로세스가 종료되고 실행중인 모든 스레드가 갑자기 종료됩니다. 원하는 것이 아니라면 시작한 스레드가 종료 될 때까지 기다려야합니다. 대부분의 언어는이를 정상적으로 수행 할 수있는 방법을 제공합니다. C#에서는 thread.Join()을 사용할 수 있습니다. –

1

어셈블리 언어 수준이나 OS 수준을 의미합니까?

어셈블리 측면에서 보면 메서드를 호출 할 때 모든 인수가 스택에 푸시되고 마지막으로 메서드 주소가 추가됩니다 (가상 인 경우 추가 테이블 조회가 있음). 그런 다음 코드는 메서드의 주소에서 "ret"명령이 실행될 때까지 계속되며 호출이 수행 된 곳에서 실행이 다시 시작됩니다. 어셈블리를 연구하고 C가 그 프로세스에 대해 잘 이해할 수 있도록 컴파일해야합니다.

OS 레벨에서 메소드를 호출하는 데 특별한 것은 없습니다. 모든 OS는 프로세스에 CPU 시간을 할당하고 그 프로세스는 그 시간 동안 원하는 것을 수행하고 메소드를 호출하는 등의 책임을집니다. 그러나 스레드 간 전환은 CPython과 같은 소프트웨어 스레드를 사용하는 것과 달리 OS에서 수행됩니다.

0

TestClass t = new A();

나는 여기에 새로운 TestClass()를 의미한다고 생각합니다.

컴파일러는이 코드를 Java 바이트 코드로 변환합니다. 다음은 article에서 "Java 가상 머신이 메소드 호출을 처리하고을 리턴하는 방법"에서 발췌 한 것입니다. Java 가상 머신 클래스의 메소드를 호출 할 때

는 항상 컴파일시 공지 인 오브젝트 참조의 종류에 따라 호출 방법 선택한다. 의 경우 가상 컴퓨터가 인스턴스 메서드를 호출 할 때 개체의 실제 클래스를 기반으로 호출 할 메서드를 선택합니다.이 메서드는 런타임시에만 이 될 수 있습니다. 클래스 메소드 인스턴스 메소드 invokevirtual과 invokestatic (이) :

JVM은 두 가지 다음 표에 도시 된 명령어들, 방법의 두 가지 종류 호출을 사용한다.

방법 invokevirtual의 호출과 팝는 objectref와 인수 indexbyte2 invokestatic (이) 연산 코드 오퍼랜드 설명

invokevirtual indexbyte1, 팝 인수 indexbyte2 상수 풀에 인덱스

invokestatic (이) indexbyte1에있어서 호출 , 상수 풀 인덱스에서 정적 메서드 호출

+0

예. 정정했다. 감사. – Sandbox

4

함수 호출은 본질적으로 goto 문입니다. 단, 끝에는 호출 된 곳으로 되돌아 가야합니다.

"돌아 오는"위치 정보를 기본적으로 보유하고있는 함수 호출 스택이 있습니다.

함수 호출이 필요합니다

  • 저장소 (푸시) 당신이 일을 끝낼 때 사용하는 호출 된 함수의 스택에 현재 명령어의 위치.
  • 모든 매개 변수를 스택에도 넣습니다.
  • 호출 된 함수의 첫 번째 명령에 이동합니다.

호출 된 함수가 매개 변수를 읽어야 할 때 스택에서 읽습니다.

호출 된 함수가 완료되거나 "return"문을 친 경우 반환해야 할 주소를 찾고 "goto"를 반환합니다. t.B(3, 4)이 무엇

+0

레지스터에 레지스터가 거의 없기 때문에 스택의 모든 매개 변수를 스택에 저장하는 것이 일반적인 방법이지만 x86-64를 비롯한 대부분의 아키텍처에서는 처음 몇 개의 인수가 메모리 대신 CPU 레지스터에 전달됩니다. – han

0

:


push ebp // save EBP state, the caller will need it later 
mov ebp, esp // save ESP state 
// push registers I would use but EAX, I'm not using any 
sub esp, 4 // alloc 4 bytes in the stack to store "result" 
mov dword ptr [ebp-4], 1 // result = 1 (true) 
mov eax, dword ptr [ebp-4] // prepares return value o be "result" 
add esp, 4 // frees allocked space 
// pop registers 
mov esp, ebp 
pop ebp 
ret 

개체 구현 :


push 4 
push 3 
call B 
add esp, 8 // release memory used 

call 잘 스택에 호출 후에 명령어의 주소를 못살게 굴지는 다음 주소) (B로 처리 스레드를 점프 공유됩니다. 새 오브젝트를 선언하면 모든 오브젝트 변수가 저장됩니다. 이 경우 아무 것도 참조되지 않습니다.

여러 스레드에 관해서는 스레드 메모리가 분리되어 있습니다. 커널이 프로세서를 다른 스레드로 전환 할 때 스레드 흐름에서 실제로 아무 일도 일어나지 않습니다. 커널은이 흐름을 멈추고 재개합니다.

+0

ebp? 특히? Eax? am lost – Sandbox

+0

@Sandbox : ebp, esp 및 eax는 프로세서에서 사용되는 일부 레지스터의 이름입니다. http://en.wikipedia.org/wiki/X86#Purpose를 참조하십시오. –

2

(가정 86) 먼저 당신은 스택을 이해해야한다. 함수는 "스택"이라는 메모리 영역을 사용합니다. 각 플레이트에는 DWORD (32 비트) 데이터가 포함되어있는 플레이트 스택처럼 생각할 수 있습니다. CPU에는 스택의 현재 위치 (가상 메모리 주소 일뿐)를 기록하는 레지스터가 있습니다. 스택 포인터라고하며 일반적으로 esp 레지스터에 저장됩니다.

기능은 스택과 상호 작용

, 그들은 일반적으로 두 가지 중 하나를 수행 같습니다 푸시 또는 합니다. "밀어 넣기"는 스택 포인터를 스택 상단에 놓을 때 스택 포인터를 다음으로 가장 높은 위치로 이동시킨 다음 그 새로운 위치 (새로운 최상위 위치)로 복사하는 것으로 구성됩니다. 밀어 넣기는 더 많은 데이터가 저장되어 있기 때문에 "스택을 증가시킵니다"(더 많은 플레이트).

"팝"은 스택의 최상위 항목이 현재 스택 상단 (esp 레지스터가 가리키고있는 것)에서 cpu 레지스터 (일반적으로 eax) 스택 포인터를 스택 아래쪽의 한 위치로 이동합니다.

이제 함수를 호출하도록 설정할 수 있습니다.

코드

t.B(3, 4); 

조립체 발생

// here is a push we described above. The function we are in currently is 
// pushing the value "4" onto the stack. This is one of the arguments to the 
// B function we are calling. Note that we push the last argument first 
push 4 
// here is another push. This time we are pushing the next argument to the 
// B function 
push 3 
call B // this call sets up the context for the next function to run 

우리 함수 현재 함수의 문맥 전환되어 호출된다. 함수가 실행할 필요가있는 여분의 peices 정보는 우리가 스택에 푸시 한 인수입니다.

새로운 함수는 스택에있는 로컬 변수를위한 공간을 만들기 위해 스택을 유지하고 스택 포인터를 레지스터에 저장하여 함수가 반환되면 되돌릴 수 있도록합니다. 이것이 발생하지 않는다면 호출 함수는 이전에 스택에 놓인 물건에 어떻게 접근할지 모르는 제어권을 회복 할 때 모든 방향이 혼란 스러울 것입니다. 예를 들어 자신의 로컬 변수 나 스택 포인터의 컨텍스트와 같은 것입니다. 함수를 호출했습니다.

여기 어셈블리가 진행 중입니다 (Havenard에서 이것을 훔치기).

// Here is the B function making sure that the calling function can get back to 
// the it's stack context when B returns. 
push ebp 
mov ebp, esp 
// remember when I said that a push was growing the stack. Well you can also grow 
// it just by moving the stack pointer higher, as if there were already more plates there 
// you may wonder why we are subtracting (sub) from the stack pointer (esp) to grow it 
// the reason is that the stack "grows down" in memory. In other words, as the stack grows 
// the memory addresses of the stack grow smaller. 
// the reason we are subtracting 4 is because we only need to grow the stack by one plate 
// so that we can store the local variable 'result' there. If we had 2 local variables 
// we would have subtracted 8 
sub esp, 4 
// the instructions below are simply moving the static value 1 into the local variable 
// 'result'. Local variables are always referenced relative to the bottom of the stack 
// context for the current function. This value is stored in the ebp register, which we 
// saw earlier in the function setup above. 
// so now we think of the location where the 'result' variable is stored as "ebp-4" 
// we know that because we put it there. 
mov dword ptr [ebp-4], 1 // result = 1 (true) 
// eax is a special register that contains the return value of the function. That is why 
// you see the value of 'result' (which we know as [ebp-4] in the eax register 
mov eax, dword ptr [ebp-4] 
// We adjust the stack pointer back to it's previous location 
// before we subtracted to make room for our local variable 
add esp, 4 
// Our work is done now.. time to clean stuff up for our calling function and 
// leave things as we found them. Our trusty ebp register stores the old stack pointer 
// that our calling function needs to resume it's stack context. 
mov esp, ebp 
pop ebp 
ret 

은 내가 특히 B 함수에서 반환에 탈락 한 몇 가지 세부 사항이 있습니다 확신 해요,하지만 내 생각은 꽤 좋은 개요입니다.

관련 문제