이렇게 큰 프로젝트를 본 적이 없는 초보자에게는 모듈과 다양한 정의된 데이터 구조를 모두 버리십시오. 너무 지저분해서 다양한 데이터 구조의 의미가 나를 미치게 만듭니다. 글쎄요, 구조가 형편없다고 말하는 것이 아닙니다. 오히려 코드가 환상적이라고 생각합니다. . 잠시 동안만요. . 받아들일 수 없습니다. 헛소리는 그만하고 문제를 시작하세요, 아아╮(╯▽╰)╭
첫 번째 포인트: ngx_cdecl
<code><span>int</span> ngx_cdecl main(<span>int</span> argc, <span>char</span> *<span>const</span> *argv);</code>
네, 이게 ngx_cdecl입니다. 예전부터 알고 있었어요. 소스코드에 __cdecl, __stdcall 같은게 있는데 여기서 ngx_cdecl은 뭐죠? 논리적으로는 똑같지만 정의를 찾아보면 이렇습니다.
<code><span>#<span>define</span> ngx_cdecl</span></code>
그렇습니다. 비어있습니다. 정의하세요. 그 용도는 무엇인가요? 물론 유용하고 아주 잘 사용됩니다. 그렇지 않고서 어떻게 좋은 코드라고 말할 수 있겠습니까? 앞으로 구멍을 메우지 않도록 탈출구를 남겨 두는 것이 좋습니다. 이 매크로는 크로스 플랫폼 지원과 함수 호출 방법 조정을 용이하게 하기 위해 nginx에서 사용됩니다. 문제가 발생하면 위의 정의를 다음과 같이 수정할 수 있습니다.
<code><span>#<span>define</span> ngx_cdecl stdcal</span></code>
코드를 더 많이 읽는 것이 좋다고 어떻게 말할 수 있을까요? 좋아요, 이제 cdecl과 stdcall을 설명해 보겠습니다.
__cdecl: C 언어의 기본 함수 호출 방법을 나타내는 C Declaration의 약어입니다. 모든 매개변수는 오른쪽에서 왼쪽으로 스택에 푸시됩니다. 이러한 매개변수는 호출자에 의해 지워집니다. 이를 수동 스택 지우기라고 합니다. 호출된 함수에서는 호출자가 몇 개의 매개변수를 전달하도록 요구하지 않습니다. 호출자는 컴파일 시간 오류를 발생시키지 않고 너무 많거나 너무 적은 매개변수 또는 완전히 다른 매개변수를 전달합니다.
프로그램이 정상적으로 실행되려면 함수를 호출하는 코드와 호출되는 함수가 동일한 함수 호출 규칙을 사용해야 합니다.
__cdecl과 __stdcall의 차이점: __cdecl은 호출자 정리 매개변수가 차지하는 스택이고, __stdcall은 호출된 함수 정리 매개변수가 차지하는 스택입니다. 함수 A가 __stdcall이고 함수 B가 함수 A를 호출한다고 가정합니다. 함수 선언을 통해 함수 A가 __stdcall임을 컴파일러에 알려야 합니다. 컴파일러는 자연스럽게 올바른 호출 코드를 생성합니다. 함수 A가 __stdcall이지만 함수 A를 참조할 때 함수 A가 __cdecl 모드에 있음을 컴파일러에 알리면 컴파일러는 함수 A의 호출 규칙과 일치하지 않는 __cdecl 모드에서 코드를 생성하므로 오류가 발생합니다.
참고: __stdcall의 호출된 함수는 컴파일 중에 전달된 매개변수의 정확한 수를 알아야 하므로(호출된 함수는 스택을 지워야 함) printf와 같은 가변 매개변수 함수는 지원될 수 없습니다. 그리고 호출자가 잘못된 수의 매개변수를 사용하면 스택 오류가 발생합니다.
두 번째 점은 ngx_int_t & ngx_uint_t입니다
<code><span>typedef</span> intptr_t ngx_int_t; <span>typedef</span> uintptr_t ngx_uint_t;</code>
stdint.h에서 intptr의 정의를 찾으세요.
<code><span>117</span><span>/* Types for `void *' pointers. */</span><span>118</span><span>#<span>if</span> __WORDSIZE == 64 </span><span>119</span><span># ifndef __intptr_t_defined </span><span>120</span> typedef <span>long</span><span>int</span> intptr_t; <span>121</span><span># <span>define</span> __intptr_t_defined </span><span>122</span><span># <span>endif</span></span><span>123</span> typedef unsigned <span>long</span><span>int</span> uintptr_t; <span>124</span><span>#<span>else</span></span><span>125</span><span># ifndef __intptr_t_defined </span><span>126</span> typedef <span>int</span> intptr_t; <span>127</span><span># <span>define</span> __intptr_t_defined </span><span>128</span><span># <span>endif</span></span><span>129</span> typedef unsigned <span>int</span> uintptr_t; <span>130</span><span>#<span>endif</span></span></code>
정의의 주석은 다음을 나타냅니다. 포인터의 길이가 항상 긴 정수형과 동일하기 때문에 두 가지 유형을 포인터로 사용하도록 정의한 것입니다. 포인터는 배열 인덱스이므로 시스템 커널에서는 메모리를 큰 배열로 처리합니다. 메모리 포인터는 배열 인덱스/첨자입니다. 커널 프로그래머는 이 특수 정수 유형을 사용하여 메모리 주소 값을 받아들이고 포인터를 사용하는 것보다 더 직관적으로 메모리를 작동하며 실수할 가능성이 적습니다.
intptr_t 이 유형은 void *와 정수 사이에서 안전하게 변환될 수 있으며, 이는 64비트 플랫폼에서 프로그램을 작성하는 데 매우 중요합니다. 즉, 포인터를 정수로 연산해야 할 경우 intptr_t로 변환하는 것이 안전하며, 연산이 완료된 후 다시 포인터 유형으로 변환하면 포인터 역참조로 인해 발생하는 버그를 방지할 수 있습니다.
코드의 매크로에서 볼 수 있듯이 intptr_t의 길이는 다양한 플랫폼에 맞게 조정됩니다. 컴파일 환경이 64비트인 경우 intptr_t는 long int이고 그렇지 않으면 int입니다.
그렇다면 왜 nginx에서 ngx_int_t를 typedef하는 데 사용하고 싶나요? 왜 int를 사용하지 않습니까?
유형 이름으로 보면 일반적인 int 유형이라는 것을 이해하기 쉽습니다. intptr은 플랫폼에 따라 정의되고 길이가 변경되지 않기 때문에 nginx가 사용하는 것 같습니다. 다시 정의해야 합니다.
위는 nginx 소스코드의 첫 번째 읽기를 소개합니다. (1) - 내용을 포함하여 메인부터 시작하겠습니다. PHP 튜토리얼에 관심이 있는 친구들에게 도움이 되길 바랍니다.