[펌] Windows System Call 원리..근데 영어네..쩝...

How do Windows NT System Calls REALLY work?


Most texts that describe Windows NT system calls keep many of the important details in the dark. This leads to confusion when trying to understand exactly what is going on when a user-mode application "calls into" kernel mode. The following article will shed light on the exact mechanism that Windows NT uses when switching to kernel-mode to execute a system service. The description is for an x86 compatible CPU running in protected mode. Other platforms supported by Windows NT will have a similar mechanism for switching to kernel-mode.

By John Gulbrandsen 8/19/2004
John.Gulbrandsen@SummitSoftConsulting.com

What is kernel-mode?

Contrary to what most developers believe (even kernel-mode developers) there is no mode of the x86 CPU called "Kernel-mode". Other CPUs such as the Motorola 68000 has two processor modes "built into" the CPU, i.e. it has a flag in a status register that tells the CPU if it is currently executing in user-mode or supervisor-mode. Intel x86 CPUs do not have such a flag. Instead, it is the privilege level of the code segment that is currently executing that determines the privilege level of the executing program. Each code segment in an application that runs in protected mode on an x86 CPU is described by an 8 byte data structure called a Segment Descriptor. A segment descriptor contains (among other information) the start address of the code segment that is described by the descriptor, the length of the code segment and the privilege level that the code in the code segment will execute at. Code that executes in a code segment with a privilege level of 3 is said to run in user mode and code that executes in a code segment with a privilege level of 0 is said to execute in kernel mode. In other words, kernel-mode (privilege level 0) and user-mode (privilege level 3) are attributes of the code and not of the CPU. Intel calls privilege level 0 "Ring 0" and privilege level 3 "Ring 3". There are two more privilege levels in the x86 CPU that are not used by Windows NT (ring 1 and 2). The reason privilege levels 1 and 2 are not used is because Windows NT was designed to run on several other hardware platforms that may or may not have four privilege levels like the Intel x86 CPU.

The x86 CPU will not allow code that is running at a lower privilege level (numerically higher) to call into code that is running at a higher privilege level (numerically lower). If this is attempted a general protection (GP) exception is automatically generated by the CPU. A general protection exception handler in the operating system will be called and the appropriate action can be taken (warn the user, terminate the application etc). Note that all memory protection discussed above, including the privilege levels, are features of the x86 CPU and not of Windows NT. Without the support from the CPU Windows NT cannot implement memory protection like described above.

Where do the Segment Descriptors reside?

Since each code segment that exists in the system is described by a segment descriptor and since there are potentially many, many code segments in a system (each program may have many) the segment descriptors must be stored somewhere so that the CPU can read them in order to accept or deny access to a program that wishes to execute code in a segment. Intel did not choose to store all this information on the CPU chip itself but instead in the main memory. There are two tables in main memory that store segment descriptors; the Global Descriptor Table (GDT) and the Local Descriptor Table (LDT). There are also two registers in the CPU that holds the addresses to and sizes of these descriptor tables so that the CPU can find the segment descriptors. These registers are the Global Descriptor Table Register (GDTR) and the Local Descriptor Table Register (LDTR). It is the operating system''s responsibility to set up these descriptor tables and to load the GDTR and LDTR registers with the addresses of the GDT and LDT respectively. This has to be done very early in the boot process, even before the CPU is switched into protected mode, because without the descriptor tables no memory segments can be accessed in protected mode. Figure 1 below illustrates the relationship between the GDTR, LDTR, GDT and the LDT.

Since there are two segment descriptor tables it is not enough to use an index to uniquely select a segment descriptor. A bit that identifies in which of the two tables the segment descriptor resides is necessary. The index combined with the table indicator bit is called a segment selector. The segment selector format is displayed below.

As can be seen in figure 2 above, the segment selector also contains a two-bit field called a Requestor Privilege Level (RPL). These bits are used to determine if a certain piece of code can access the code segment descriptor that the selector points to. For instance, if a piece of code that runs at privilege level 3 (user mode) tries to make a jump or call code in the code segment that is described by the code segment descriptor that the selector points to and the RPL in the selector indicates that only code that runs at privilege level 0 can read the code segment a general protection exception occurs. This is the way the x86 CPU can make sure that no ring 3 (user mode) code can get access to ring 0 (kernel-mode) code. In fact, the truth is slightly more complicated than this. For the information-eager please see the further reading list, "Protected Mode Software Architecture" for the details of the RPL field. For our purposes it is enough to know that the RPL field is used for privilege checks of the code trying to use the segment selector to read a segment descriptor.

Interrupt gates

So if application code running in user-mode (at privilege level 3) cannot call code running in kernel-mode (at privilege level 0) how do system calls in Windows NT work? The answer again is that they use features of the CPU. In order to control transitions between code executing at different privilege levels, Windows NT uses a feature of the x86 CPU called an interrupt gate. In order to understand interrupt gates we must first understand how interrupts are used in an x86 CPU executing in protected mode.

Like most other CPUs, the x86 CPU has an interrupt vector table that contains information about how each interrupt should be handled. In real-mode, the x86 CPU''s interrupt vector table simply contains pointers (4 byte values) to the Interrupt Service Routines that will handle the interrupts. In protected-mode, however, the interrupt vector table contains Interrupt Gate Descriptors which are 8 byte data structures that describe how the interrupt should be handled. An Interrupt Gate Descriptor contains information about what code segment the Interrupt Service Routine resides in and where in that code segment the ISR starts. The reason for having an Interrupt Gate Descriptor instead of a simple pointer in the interrupt vector table is the requirement that code executing in user-mode cannot directly call into kernel-mode. By checking the privilege level in the Interrupt Gate Descriptor the CPU can verify that the calling application is allowed to call the protected code at well defined locations (this is the reason for the name "Interrupt Gate", i.e. it is a well defined gate through which user-mode code can transfer control to kernel-mode code).

The Interrupt Gate Descriptor contains a Segment Selector which uniquely defines the Code Segment Descriptor that describes the code segment that contains the Interrupt Service Routine. In the case of our Windows NT system call, the segment selector points to a Code Segment Descriptor in the Global Descriptor Table. The Global Descriptor Table contains all Segment Descriptors that are "global", i.e. that are not associated with any particular process running in the system (in other words, the GDT contains Segment Descriptors that describe operating system code and data segments). See figure 3 below for the relationship between the Interrupt Descriptor Table Entry associated with the ''int 2e'' instruction, the Global Descriptor Table Entry and the Interrupt Service Routine in the target code segment.

Back to the NT system call

Now after having covered the background material we are ready to describe exactly how a Windows NT system call finds its way from user-mode into kernel-mode. System calls in Windows NT are initiated by executing an "int 2e" instruction. The ''int'' instructor causes the CPU to execute a software interrupt, i.e. it will go into the Interrupt Descriptor Table at index 2e and read the Interrupt Gate Descriptor at that location. The Interrupt Gate Descriptor contains the Segment Selector of the Code Segment that contains the Interrupt Service Routine (the ISR). It also contains the offset to the ISR within the target code segment. The CPU will use the Segment Selector in the Interrupt Gate Descriptor to index into the GDT or LDT (depending on the TI-bit in the segment selector). Once the CPU knows the information in the target segment descriptor it loads the information from the segment descriptor into the CPU. It also loads the EIP register from the Offset in the Interrupt Gate Descriptor. At this point the CPU is almost set up to start executing the ISR code in the kernel-mode code segment.

The CPU switches automatically to the kernel-mode stack

Before the CPU starts to execute the ISR in the kernel-mode code segment, it needs to switch to the kernel-mode stack. The reason for this is that the kernel-mode code cannot trust the user-mode stack to have enough room to execute the kernel-mode code. For instance, malicious user-mode code could modify its stack pointer to point to invalid memory, execute an ''int 2e'' instruction and thereby crash the system when the kernel-mode functions uses the invalid stack pointer. Each privilege level in the x86 Protected Mode environment therefore has its own stack. When making function calls to a higher-privileged level through an interrupt gate descriptor like described above, the CPU automatically saves the user-mode program''s SS, ESP, EFLAGS, CS and EIP registers on the kernel-mode stack. In the case of our Windows NT system service dispatcher function (KiSystemService) it needs access to the parameters that the user-mode code pushed onto its stack before it called ''int 2e''. By convention, the user-mode code must set up the EBX register to contain a pointer to the user-mode stack''s parameters before executing the ''int 2e'' instruction. The KiSystemService can then simply copy over as many arguments as the called system function needs from the user-mode stack to the kernel-mode stack before calling the system function. See figure 4 below for an illustration of this.

What system call are we calling?

Since all Windows NT system calls use the same ''int 2e'' software interrupt to switch into kernel-mode, how does the user-mode code tell the kernel-mode code what system function to execute? The answer is that an index is placed in the EAX register before the int 2e instruction is executed. The kernel-mode ISR looks in the EAX register and calls the specified kernel-mode function if all parameters passed from user-mode appears to be correct. The call parameters (for instance passed to our OpenFile function) are passed to the kernel-mode function by the ISR.  

Returning from the system call

Once the system call has completed the CPU automatically restores the running program''s original registers by executing an IRET instruction. This pops all the saved register values from the kernel-mode stack and causes the CPU to continue the execution at the point in the user-mode code  next after the ''int 2e'' call.

Experiment

By examining the Interrupt Gate Descriptor for entry 2e in the Interrupt Descriptor Table we can confirm that the CPU finds the Windows NT system service dispatcher routine like described in this article. The code sample for this article contains a debugger extension for the WinDbg kernel-mode debugger that dumps out a descriptor in the GDT, LDT or IDT.    

Download the example code: ProtMode.zip

The WinDbg debugger extension is a DLL called ''protmode.dll'' (Protected Mode). It is loaded into WinDbg by using the following command: ".load protmode.dll" after having copied the DLL into the directory that contains the kdextx86.dll for our target platform. Break into the WinDbg debugger (CTRL-C) once you are connected to your target platform. The syntax for displaying the IDT descriptor for ''int 2e'' is "!descriptor IDT 2e". This dumps out the following information:

kd>!descriptor IDT 2e
------------------- Interrupt Gate Descriptor --------------------
IDT base = 0x80036400, Index = 0x2e, Descriptor @ 0x80036570
80036570 c0 62 08 00 00 ee 46 80
Segment is present, DPL = 3, System segment, 32-bit descriptor
Target code segment selector = 0x0008 (GDT Index = 1, RPL = 0)
Target code segment offset = 0x804662c0
------------------- Code Segment Descriptor --------------------
GDT base = 0x80036000, Index = 0x01, Descriptor @ 0x80036008
80036008 ff ff 00 00 00 9b cf 00
Segment size is in 4KB pages, 32-bit default operand and data size
Segment is present, DPL = 0, Not system segment, Code segment
Segment is not conforming, Segment is readable, Segment is accessed
Target code segment base address = 0x00000000
Target code segment size = 0x000fffff

The ''descriptor'' command reveals the following:

  • The descriptor at index 2e in the IDT is at address 0x80036570.
  • The raw descriptor data is C0   62 08 00 00 EE 46 80.
  •  This means that:
    • The segment that contains the Code Segment Descriptor described by the Interrupt Gate Descriptor''s Segment Selector is present.
    • Code running at least privilege level 3 can access this Interrupt Gate.
    • The Segment that contains the interrupt handler for our system call (2e) is described by a Segment Descriptor residing at index 1 in the GDT.
    • The KiSystemService starts at offset 0x804552c0 within the target segment.

The "!descriptor IDT 2e" command also dumps out the target code segment descriptor at index 1 in the GDT. This is an explanation of the data dumped from the GDT descriptor:

  • The Code Segment Descriptor at index 1 in the GDT is at address 0x80036008.
  • The raw descriptor data is FF FF 00 00 00 9B CF 00.
  • This means that:
    • The size is in 4KB pages. What this means is that the size field (0x000fffff) should be multiplied with the virtual memory page size (4096 bytes) to get the actual size of the segment described by the descriptor. This yields 4GB which happens to be the size of the full address space which can be accessed from kernel-mode. In other words, the whole 4GB address space is described by this segment descriptor. This is the reason kernel-mode code can access any address in user-mode as well as in kernel-mode.
    • The segment is a kernel-mode segment (DPL=0).
    • The segment is not conforming. See further reading, "Protected Mode Software Architecture" for a full discussion of this field.
    • The segment is readable. This means that code can read from the segment. This is used for memory protection. See further reading, "Protected Mode Software Architecture" for a full discussion of this field.
    • The segment has been accessed. See further reading, "Protected Mode Software Architecture" for a full discussion of this field.

To build the ProtMode.dll WinDbg debugger extension DLL, open the project in Visual Studio 6.0 and click build. For an introduction of how to create debugger extensions like ProtMode.dll, see the SDK that comes with the "Debugging Tools for Windows" which is a free download from Microsoft.

Further Reading

For information on the Protected Mode of the Intel x86 CPU there are two great sources:

1)      "Intel Architecture Software Developers Manual, Volume 3 - System Programming Guide". Available from Intel''s web site in PDF format.

2)      "Protected Mode Software Architecture" by Tom Shanley. Available from Amazon.com (published by Addison Wesley).

For more programming details about the x86 CPU, must-haves are:

1)              Intel Architecture Software Developers Manual, Volume 1 - Basic Architecture.

2)              Intel Architecture Software Developers Manual, Volume 2 - Instruction Set Reference Manual.

      Both these books are available in PDF format on the Intel web site (you can also get a free hardcopy of these two books. Volume 3 is however only available in PDF format).

About the Author

John Gulbrandsen is the founder and president of Summit Soft Consulting. John has a formal background in Microprocessor-, digital- and analog- electronics design as well as in embedded and Windows systems development. John has programmed Windows since 1992 (Windows 3.0). He is as comfortable with programming Windows applications and web systems in C++, C# and VB as he is writing and debugging Windows kernel mode device drivers in  SoftIce.  

To contact John drop him an email: John.Gulbrandsen@SummitSoftConsulting.com

About Summit Soft Consulting

Summit Soft Consulting is a Southern California-based consulting firm specializing in Microsoft''s operating systems and core technologies. Our specialty is Windows Systems Development including kernel mode and NT internals programming.

To visit Summit Soft Consulting on the web: http://www.summitsoftconsulting.com/

[펌]중요 string.h 함수

char* strcpy(char* p, const char *q); // q를 p로 복사한다(종료문자까지포함)

char* strcat(char* p, const char *q); // q를 p에 추가한다(종료문자까지 포함)

char* strncpy(char* p, const char *q, int n); // q에서 p로 n문자를 복사한다.

char* strncat(char* p, const char *q, int n); //q에서 p에 n문자를 추가한다.



size_t strlen(const char* p); //  p의 길이(종료문자는 세지 않는다)

int strcmp(const char* p, cosnt char* q); // 비교: p및 q

int strncmp(const char* p, const char* q, int n) //처음 n문자를 비교한다.


char* strchr(char *p, int c); // p에서 처음 c를 찾는다.

const char* strchr(const char* p, int c);

char* strrchr(char* p, int c); // p 에서 마지막 c를 찾는다.

const char* strrchr(const char* p, int c);

char* strstr(char* p, const char* q); // p에서 처음 q를 찾는다

const char* strstr(const char* p, const char* q);


char* strpbrk(char* p, const char* q); // q안에 있는 아무 문자와 일치하는 p 내의 첫 위치를 찾는다.

const char* strpbrk(const char* p, const char* q);


size_t strspn(const char* p, const char* q); // q에 없는 문자가 처음으로 p에 나타날 때까지의 문자개수

size_t strcspn(const char*p, const char * q); // q에 있는 문자가 처음으로 p에 나타날 때까지의 문자개수

vi 좋은것들...

vi는 크게 세 가지 모드로 나뉘어집니다. 우선 명령모드로 키 입력이 바로 명령이 되는 모드, 다음은 입력모드로 실제 문서를 편집하는 모드, 마지막으로 ex 모 드로 ex명령을 실행시키는 모드입니다. vi를 실행시키면 처음에는 명령모드 상태에 있게 됩니다. 명령모드에서 : 키를 누르면 ex모드로, a, i 등의 키를 누르면 입력모 드로 전환되어 문서를 편집할 수 있는 상태로 됩니다. 본격적인 내용으로 들어가기 전에 기본적인 편집에 대해서 설명을 하겠습니 다. 특정 파일을 편집하기 위해 열려면 vi [filename]을 shell prompt에서 실행시킵 니다. 이렇게 연 파일을 편집모드로 들어가 편집하려면 i를 입력, 원하는 내용을 입 력한 후 종료하려면 [ESC]ZZ 나 [ESC]:wq를 누르면 저장 후 종료하게 됩니다. 이 정 도는 기본적으로 알아두셔야 지금부터 하는 내용을 따라해 보실 수 있겠죠? 또 기본 적으로 알아두셔야 할 명령이 명령모드에서 u 즉 undo명령입니다. 그래야 잘못 실행 된 명령을 바로 undo할 수 있겠죠...^^;

현재위치에서 삽입 i
현재줄의 처음 위치에서 삽입 I
현재위치에서 추가 a
현재줄의 끝에서 추가 A
새로운 한 줄을 커서 아래줄에 연다. o
새로운 한 줄을 커서 위줄에 연다. O
줄을 지우고 삽입모드로 S
현재 위치에서 Relpace 모드로 R
다음줄과 현재줄을 합친다. J
대문자<->소문자 ~
마지막 명령을 반복한다. .
마지막 수정한 것을 취소한다. u
줄을 처음 상태로 복구한다. U

2.1 편집
2.1.1 복사, 붙이기, 삭제, 문자 치환
텍스트 편집기를 사용하면서 가장 유용한 기능 중에 하나가 반복되는 문자열 을 복사해서 다른 곳에 붙이는 기능이라고 생각합니다. vi 역시 이러한 기능을 가지 고 있고 vi의 명령어의 특성상 매우 많은 방법으로 이러한 명령을 수행할 수 있습니 다. 명령모드에서 yy라고 입력하면 현재 커서가 위치한 한 줄을 카피(Yanking)하게 됩니다. 또 dd는 현재 행을 삭제하게 되는데 이 역시 삭제가 이루어지면서 버퍼에 복 사가 되어있습니다. yy나 dd로 버퍼에 들어간 내용을 붙이려면 붙이고자 하는 곳 한 줄 위에서 p를 입력하면 됩니다. 짐작하시듯이 y키가 복사를 하는데 사용되는 키이고 d가 삭제에, p가 붙이기에 사용되는 키입니다. 명령모드에서 사용되는 명령들은 명령 앞에 숫자를 입력한 후 명령을 내리면 그 횟수만큼 명령을 반복하게 됩니다. 쉽게 2yy 라고 입력하면 2줄을 복사하리라는 것을 생각할 수 있습니다. 뒤에서 나오게 되 는 이동에 관련된 명령과 조합하면 복사나 삭제를 원하는 영역에서 해 낼 수 있습니 다. 우선 여기서는 복사에 사용되는 키가 y키, 삭제에 사용되는 키가 d, 붙이기에 사 용되는 키가 p키라는 것만 숙지하고 넘어갑시다. 참 한가지 빠뜨리고 넘어갈 뻔했군 요. X 나 x키가 명령모드에서 삭제에 사용됩니다. 문자 위에서 x를 누르면 위치한 문 자가 삭제되고 X를 누르면 [bs]키처럼 동작을 합니다.

Copy delete yanking
한 단어 cw dw yw
두 단어 2cw or c2w 2dw or d2w 2yw or y2w
한 행 cc dd yy
커서 위치에서 행의 끝까지 c$ or C d$ or D y$ or Y
커서 위치에서 행의 처음까지 c0 d0 y0
한 문자 변경 r x or X y1 or yh
한 파일에서 문자치환
 : 전체영역의 문자치환 -> :s/old/new/g
 : 특정 줄의 문자 치환  -> :s,d s/old/new
 : 특정라인의 특정문자위치의 문자치환 -> :[s,d]s/old/new


2.1.2 Named 버퍼 사용
보통의 에디터의 경우에는 어떤 특정 문자열을 새로 복사하면 이전에 복사했 던 문자열이 지워지게 됩니다. vi는 a-z까지의 이름을 갖는 버퍼에 각각을 유지하여 복사 해 둘 수 있는 매우 편리한 기능을 제공합니다. 이러한 기능을 named buffer라 하고 사용방법은 모든 명령모드에서의 명령 앞에 a~z까지의 버퍼명 "a ~ "z을 붙여주 는 것입니다. 위에서 복사를 했던 것을 다시 해보면 "ayy를 누르면 현재 줄이 "a 버 퍼에 복사가 됩니다. 이것을 다시 붙이기를 하려면 "ap를 입력하면 되겠지요. 이미 있는 버퍼에 내용을 추가하거나 처음부터 버퍼에 써 넣는 방법을 설명 하겠습니다. 이미 어떤 버퍼에 내용이 들어가 있고 그 뒤에 내용을 추가하고 싶은 경 우가 생기죠. 이런 경우 추가는 버퍼 이름의 대문자를 사용합니다. "Ayy와 같이 현재 줄을 버퍼에 추가하거나, ex모드에서 특정 문자열이 있는 줄을 모두 복사해두고 싶은 경우 :g/string/y A 이런 식으로 사용하는 것이죠. 복사등을 하지 않고 버퍼에 직접 써넣으려면 q명령을 사용합니다. q 다음에 버퍼명을 누르면 화면하단에 recording이라는 표시가 나오고 지금부터 하는 키 입력 은 선택한 버퍼명에 들어가게 됩니다. 키입력이 끝나면 다시 q를 누르면 됩니다. 뒤에서 ex모드에서 사용하는 방법이나 @-function에 사용할 수 있는 방법을 설명하겠습니다. 여기서는 이 정도만 알고 넘어가도록 하죠. (주: vim에서는 register라고 하는군요.)
2.1.3 여러 문서의 편집
vi에서 여러 문서를 동시에 열기 위해서는 vi [filename1] [filename2]...의 형태로 열고자 하는 파일을 뒤에 이어서 써 줍니다. 그럼 동시에 여러 문서를 편집하 게 되죠. 그리고 각 문서 사이의 전환은 :n키와 :N을 이용해서 합니다. 즉 vi test1 test2로 두 파일을 열어서 작업을 하고 있다면 :n을 입력하면 test2로 편집이 넘어가 게 됩니다. 다시 :N 또는 :prev을 입력하면 test1을 편집할 수 있습니다. 또 현재 어 떤 파일을 편집중인지 확인하려면 :args를 입력하면 현재 열린 모든 파일 중에 현재 편집중인 파일이 대괄호로 둘러싸여 표시됩니다. 열린 파일이 많아서 여러 문서를 건 너뛰어 편집해야 하는 경우에는 n이나 N앞에 skip할 파일 개수를 써주면 됩니다. :4n 이런 식이 되겠죠.
2.2 이동
문서내용을 수정하려면 편집하고자 하는 위치로 먼저 이동하여야 편집이가 능하겠지요. vi는 다른 편집기와는 비교도 되지 않을 만큼 많은 이동 방법을 제공합 니다. 이동명령은 다른 명령(복사, 삭제, 붙이기)등과 조합해서 사용하기 때문에 확 실히 알아두시는 것이 좋습니다. 제 나름대로 분류를 가까운 거리의 이동에서 원거리 이동까지 분류를 해 보았습니다.
2.2.1 짧은 이동
가장 기본적인 이동이 명령모드 상에서 h, j, k, l키를 이용한 이동입니다. 차례로 좌, 하, 상, 우로의 이동을 나타냅니다. 역시 다른 명령모드 명령과 마찬가지 로 회수를 명령 앞에 넣어서 반복할 수 있습니다. 요즘은 대부분 커서 키를 지원하고 있지만 손가락이 오가는 거리 상 사용하지 않으시는 것이 좋습니다. 단어간의 이동은 w, b, e, E키로 합니다. w키는 다음 단어의 첫 번째 문자로 이동을 합니다. b는 반대로 앞 단어의 첫 번째 문자로의 이동을 나타냅니다. e, E는 첫 번째 문자로의 이동이 아닌 단어의 마지막 문자로의 이동입니다. 역시 회수를 앞 에 넣어서 몇 단어를 한 번이 이동할 수 있습니다. 이 밖에도 현재행 상에서 이동을 하는 명령들이 많이 있습니다. 0키를 누르 면 현재 행의 맨 앞으로 이동합니다. ^ 이나 | 역시 행의 맨 처음으로 이동합니다. 0 키는 행의 처음에 공백이 나오다 문자열이 나오는 경우에는 공백을 무시하고 맨 처음 나타나는 문자로 이동합니다. 하지만 ^ 이나 |는 0번째 열로 이동하게 됩니다. 특정 열로 이동하려면 <숫자>| 즉 10번째 열로 이동하려면 10|을 입력하면 10번째 열로 이 동을 하게 됩니다. 현재 행의 끝으로 이동하려면 $기호를 사용합니다. 이제 행간의 이동입니다. 한 행 밑으로 이동은 + 또는 [enter], 위로의 이동 은 - 키를 사용합니다. 10[enter]라고 입력해 보십시오. 엔터를 한 번 입력하면 한 행 밑으로 이동하지만 한 번에 10 행씩 팍팍 떨어지지 않습니까? 10+도 같은 결과를 보여줍니다.

좌, 하, 상, 우 h, j, k, l
다음줄의 첫번째 문자로 + or [enter]
이전줄의 첫번째 문자로 -
단어의 끝으로 e, E
다음 단어로 w, W
이전 단어로 b, B
행의 끝으로 $
행의 처음으로 0 ('A' 인 경우 A앞으로 커서이동)
행의 처음으로 ^ ('A' 인 경우에도 맨앞으로 커서이동)
다음, 이전 문장의 처음으로 ), (
다음, 이전 문단의 처음으로 }, {
다음, 이전 구절의 처음으로 ]], [[


2.2.2 원거리 이동
이제 조금 원거리 이동으로 넘어갑니다. 문서가 20000행 정도 된다고 합시 다. 처음에 문서를 열게 되면 커서는 1행에 위치합니다. 에러가 10000행 정도에 발생 한 소스코드를 수정한다고 치면, 어떻게 이동하면 좋을까요? 물론 뒤에서 파일 열기 에서 배우겠지만 현재까지 저희가 언급한 바로는 10000+를 하든지 해야겠지요. 물론 이렇게 해도 되겠지만 특정 행으로 직접 이동하는 명령이 있습니다. 바로 G명령입니 다. 10G라고 입력하면 10번째 행으로 이동하게 되는 것이죠. 다른 방법으로는 ex명령 에서 :10라고 입력하고 엔터를 입력하면 그 행으로 이동하게 됩니다. 맨 처음 줄로의 이동은 gg 나 1G 나 :1 등을 사용하고 마지막 줄로의 이동은 G 나 :$ 등이 가능합니 다. 여기서 $는 뒤에서 언급을 하겠지만 ex모드에서는 마지막 줄을 나타냅니다. 명령 모드에서는 행의 끝으로 이동에 사용되었죠.

한 화면 앞으로 스크롤 ^F (means CTRL-F)
한 화면 뒤로 스크롤 ^B
반 화면 앞으로 스크롤 ^D
반 화면 뒤로 스크롤 ^U
한 줄 앞으로 스크롤 ^E
한 줄 뒤로 스크롤 ^Y
화면의 맨 위줄로 H nH인 경우 맨 위에서 n행 밑으로
화면의 중간 줄로 M
화면의 맨 아래줄로 L nL인 경우 맨 밑에서 n행 위로


2.2.3 찾기로 이동
자세한 내용의 찾기는 3장에서 다를 것이고 여기서는 이동에 사용되는 찾기 명령에 대해서 설명하도록 하겠습니다. 현재 행에서 순방향으로의 특정 문자를 찾을 때는 f 키를 사용합니다. 현재 행에서 커서위치에서 첫 번째 나타나는 o 문자까지 이 동하고 싶다면 fo를 입력하면 됩니다. 반대 방향이라면 Fo를 입력하면 되겠죠. 이 명 령의 반복은 어떻게 할까요? 다른 명령처럼 앞에 반복회수를 명시해도 되지만 찾은 후에 다시 찾기를 같은 방향으로 하려면 ; 키를 반대방향으로 하려면 , 키를 사용해 서 명령을 반복합니다. 한 행을 떠나서 순방향으로 어떤 패턴이 나타나는 곳으로 이동하려면 /pattern을 역시 반대 방향이라면 ?pattern을 입력하면 됩니다. 이 역시 같은 방향으 로 반복을 하려면 n, 반대 방향은 N키를 사용합니다.

문자열의 처음으로 앞으로 검색 /pattern
문자열의 처음으로 뒤로 검색 ?pattern
검색을 다시 반복 (같은 방향) n
검색을 다시 반복 (반대 방향) N
현재 줄에서 x가 있는 곳으로 이동 fx
현재 줄에서 x가 있는 곳으로 이동 Fx
n행 밑에서 x가 있는 곳으로 이동 tx
n행 위에서 x가 있는 곳으로 이동 Tx
줄에서 찾기를 같은 방향으로 반복 ;
줄애서 찾기를 반대 방향으로 반복 ,
행 이동 n번째 줄로 이동 nG (n이 생략되면 마지막 줄로)
:n
열 이동 n| (n이 생략되면 처음 열로)
현재 커서가 위치한 단어 찾기 * (앞 방향으로 찾는다.)
현재 커서가 위치한 단어 찾기 # (뒤로 찾는다.)

2.2.4 마크를 이용한 이동
책을 읽다 북마크를 하는 것처럼 문서를 편집하다가 특정 부분을 마크할 수 있으면 편리하겠지요. vi에서는 역시 알파벳 개수만큼의 마크를 하고 마크한 곳이나 그 줄로 바로 이동할 수 있습니다. 우선 마크를 하려면 ma ~ mz를 마크 할 곳에서 입 력합니다. 마크한 곳으로 이동은 두 가지가 가능합니다. 즉 마크한 줄과 열이 일치하 는 이동은 `a ~ `z 으로 마크한 줄의 처음으로 이동은 'a ~ 'z를 이용합니다.

현재 위치를 x 이름의 마크로 저장 mx
마크한 위치(행, 열)로 이동 `x
마크한 줄로 이동 'x
이전에 마크한 위치로 이동 ``
이전에 마크한 줄로 이동 ''

2.3 파일 저장, 열기, 종료
파일을 열 때 여러 가지 옵션을 주어서 열 수 있습니다. 즉 특정 행에 커서 가 위치하도록 열 수 있는데, 마지막 줄에 위치하려면 vi + [filename] 과 같이 열 고, n번째 행에 위치시키려면 vi +n [filename] 과 같은 방식으로 열면 됩니다. 특정 문자열이 있는 줄에 커서를 위치시키려면 vi +/pattern [filename]로 엽니다. vi +R [filename] 은 파일을 읽기 전용으로 엽니다. 파일의 저장을 살펴볼까요. 파일을 저장하면서 종료하는 방법은 맨 처음에 두 가지를 설명했었습니다. :wq 와 ZZ입니다. 다른 저장방법이라면 :x 이 명령은 파 일이 변경되었을 때에는 저장하고 종료를 하지만 아니면 그냥 종료를 합니다. ZZ와 같은 기능이 같죠. 저장과 열 때 ex모드에서 사용하는 몇 가지 기호를 알아두시면 편 리합니다. 즉 %는 작성중인 파일명을 나타냅니다. 따라서 현재 파일을 편집 중에 백 업파일을 저장하고 싶다면 :w %.bak 이런 식으로 하시면 쉽게 백업을 만들 수 있습니 다. 그리고 #는 alternative 파일을 나타낸다고 하는데 이전에 열어서 작성한 파일을 나타냅니다. vi file1 으로 편집을 하다가 :e file2로 편집 파일을 새로 연 경우 다 시 먼저의 file1을 열려면 :e # 라고 하시면 됩니다.

file 열기 vi [filename]
여러 파일 열기 vi [filename1] [filename2]
읽기 전용으로 열기 view [filename] 또는 vi -R [filename]
열고 마지막 행에 위치 vi + [filename]
파일을 열고 n 번째 행에 위치 vi +n [filename]
패턴이 나타나는 곳에 행 위치 vi +/pattern [filename]
변경된 파일이면 저장하고 종료 ZZ 또는 :x
저장하고 종료 :wq
파일의 저장 w
특정 범위만 저장 :<범위>w [filename]
특정 범위를 다른 파일에 덧붙임 :<범위>w >> [filename]

[펌] Makefile을 만들어보자

 컴파일

 

작성한 코드에 구문검사를 해서 구문에 이상이 있다면 오류를 내주어 작성한 유저에게 알려주고 만약 검사에 이상이 없다면 프로그램이 시작되기 전에 메모리에 올라간다. 이처럼 컴파일의 역할은 유저가 작성한 코드에 이상이 없는지 검사하고 코드에 작성된 변수나 함수들을 메모리에 올려주는 역할을 한다.

 

 링크

 

프로그램을 만드는 마지막 작업이 바로 링크(Link)라는 과정이다. 필요한 조각들을 모두 모으거나 어떤 부분이 빠져 있는지 알아보기 위한 과정이다. 정적 라이브러리(Static Library)를 사용할 때, 링커는 프로그램이 필요로 하는 부분을 라이브러리에서 찾아서 그냥 실행파일에다 카피해버린다. 공유 라이브러리(또는 동적 라이브러리)의 경우에는 이렇게 하는 것이 아니라 실행파일에다가 단지 "실행될 때 우선 이 라이브러리를 로딩시킬 것"이라는 메시지만을 남겨놓는다. 당연히 공유 라이브러리를 사용하면 실행파일의 크기가 작아진다. 그들은 메모리도 또한 적게 차지하며, 하드 디스크의 용량도 적게 차지한다.

링킹(linking)은 여러가지 코드와 데이터를 묶어 메모리로 로드될 수 있는 하나의 실행 가능한 파일을 만드는 작업이다.

링킹은 컴파일-타임때 행해질 수도 있고, 로드-타임(로더에 의해), 혹은 런-타임(응용 프로그램에 의해)때도 행해질 수 있다.

 

Cygwin

 

윈도우 환경에서 GNU 프로그램을 사용할 수 있는 환경을 제공하는 프로그램

http://falinux.com/win/study/cygwin/cygwin1.html

Make Utility

1. 들어가기

 

프로그래머들은 작은 프로그램을 작성할 때는 단순히 수정하고 나서 모든 파일을 다시 컴파일 하여 새로 프로그램을 만들게 된다. 그러나 좀더 규모가 큰 프로그램에서는 이런 간단한 방법이 약간의 문제를 가져오게 될 것이다. 하나의 파일만 변경하게 되더라도 다시 컴파일 해야 하는 등의 수정하고 컴파일하고 테스트하는데 많은 시간을 소모하게 될 것이다. 이것 같은 문제를 해결하기 위해서 만들어 진 것이 make 유틸리티이다. 다시 정리하자면 이 유틸리티는 큰 프로그램을 유지하는 필요한 유틸리티이며, 프로그램 중 어느 부분이 새롭게 컴파일 되어야 하는지를 자동적으로 판단해서 커맨드(컴파일러, 쉘등)를 이용하여 프로그램을 재 컴파일을 하게 된다.

 

2. 왜 사용하지?

 

프로그램을 개발하게 되면 보통 라인 수가 증가하게 되어 하나의 파일만으로 프로그램을 구성하는 것은 어려운 일이다. 그래서 보통은 각각의 기능을 작은 모듈로 나누어 여러 파일로 나누어 개발을 하게 된다. 이 파일들은 서로 관계를 가지고 있는데, 어느 하나를 필요에 의해 바꾸게 되었을 때 그 파일에 있는 모듈(함수)를 이용하는 다른 파일도 새롭게 컴파일 되어야 한다. 하지만 파일 수가 많은 경우 이를 일일이 컴파일을 하게 될 때, 그 불편함과 함께 컴파일 하지 않아도 될 것도 컴파일을 하게 될 수 있고, 컴파일 해야 할 것도 미쳐 못하게 되어 링크시 에러를 발생하게 되는 경우도 있게 된다. 이런 상황에서 자동적으로 관계 있는 것만 새롭게 컴파일 하거나, 여러 개의 입력 파일로부터 출력 파일을 생성할 때 make 유틸리티는 필요하게 된다.

3. 어떻게 사용할까?

 

make 유틸리티는 많은 기능을 제공하지만, 자동으로 입력 파일을 인식하고, 컴파일 하여 출력 파일을 생성하지는 못하기 때문에 이 것에 대한 정보를 make가 알 수 있도록 파일을 제공해야 하는데 이것이 Makefile이라는 파일이다. 이것은 make가 이해할 수 있도록 일정한 형식을 가지고 있는 스크립트 언어와 흡사하다. Makefile에서는 시스템의 쉘을 사용할 수 있으며 컴파일러등의 프로그램들을 실행할 수 있다. Makefile은 대개 프로젝트의 다른 소스 파일과 같은 디렉토리에 존재하며, 큰 프로그램에서는 각 각의 기능들을 위해 독립적인 Makefile을 사용하여 프로그램을 관리한다.

4. Makefile에 대하여… …

 

메이크파일은 기본적으로 대상(Target), 의존 관계(dependency), 명령(command)등에 대한 규칙들(rules) 이루어져 있다. 아래의 list 4-1 기본적인 메이크파일의 플랫폼이다.

 

 

Target . . .  :  Dependency . . .

 

               Command  . . .

 

.                         . . .

. . .

List 4-1. Makefile 플랫폼

Note1

 

 

 


· Target : Command에 의해서 수행 되어서 나온 결과 파일을 지정하므로, 일반적으로 목적 파일(Object file)이나 실행 파일이다.

 

· Dependency : 생성되는 파일인 대상(Target)과 이것이 의존하는 파일들의 관계를 나타낸다.

 

· Command : Command 부분에 정의된 명령들은 의존 관계(Dependency)부분에 정의된 파일의 내용이 바뀌었거나, 대상(Target) 부분에 해당하는 파일이 없을 때 이곳에 정의된 것들이 차례대로 실행된다. 일반적으로 쉘에서 쓸 수 있는 모든 명령들을 사용할 수 있으며 Bash에 기반한 쉘 스크립트도 지원한다.

 

 

4.1. Makefile 예제 보기

 

· 간단한 Makefile 만들기

 

간단하게 Makefile 만들어 보자. 우리가 만들려고 하는 프로그램은 main.c, read.c, write.c 구성되어 있고 모두 io.h을 사용하고 read.c defs.h을 더 포함하고, wirte.c buffer.h라는 라는 헤더 파일을 사용한다고 가정한다. 이들을 각각 컴파일해서 test 라는 실행 파일을 생성시킨다 아래의 list 4-2 상에서 실행 파일을 만든 것이고, List 4-3 Makefile 작성하여 만든 것이다.

 

 

 $gcc -c main.c

 $gcc -c read.c

 $gcc -c write.c

 $gcc -o test main.o read.o write.o

List 4-2. 상에서 실행 파일 만들기

 

test: main.o read.o write.o

       gcc -o test main.o read.o write.o

 

  main.o : io.h main.c

       gcc -c main.c

 

  read.o : io.h defs.h read.c

       gcc -c read.c

 

  write.o : io.h buffer.h write.c

       gcc -c write.c

 

 

  $make

  gcc -c main.c

gcc -c main.c

gcc -c main.c

gcc -o test main.o read.o write.o

 

List 4-3. Makefile 작성하여 실행 파일 만들기와 실행 화면

Note2

 

 


· 쉘 상에서 make명령을 내리면 먼저 make는 먼저 현재 디렉토리에서 makefile이라는 파일을 찾는다. 이 파일이 존재하지 않으면 다시 Makefile이라는 파일을 찾는다.전통적인 유닉스 프로그래머는 makefile을 사용한다.

 

· make Makefile의 내용을 보고, 내부적으로 어떻게 파일들이 의존하고 있는지 조사한다.

 

· 위의 예제에서는 test라는 실행 파일을 만들기 위해서는 main.o io.h, read.o io.h defs.h, 그리고 write.o io.h buffer.h의 헤더 파일과 모두 자신의 소스에 의존 관계임을 알 수 있다. 만약에 main.c를 고친다면 main.o 컴파일되어 다시 생기고, test 다시 링크되어 갱신된다. 만약 io.h 바뀌었다고 가정하면 모든 파일들이 컴파일되어서 목적 파일이 생기고, 그것들이 링크가 되어 test 생긴다

 

· 변수를 사용하여 더 간단하게 만들기

 

위의 예제에서 변수를 사용하여 더 간단하게 Makefile을 작성한 것이 List 4-4 이다.

 

# Variables make Makefiles simpler

Objects = main.o read.o write.o

 

test: $(Objects)

       gcc -o test $(Objects)

 

  main.o : io.h main.c

       gcc -c main.c

 

  read.o : io.h defs.h read.c

       gcc -c read.c

 

  write.o : io.h buffer.h write.c

       gcc -c write.c

 

List 4-4. 변수을 이용하여 Makefile만들기

Note3

 

 


· Makefile에서의 주석문은 #으로 시작하고, 한 줄의 끝까지 계속된다.

 

· Makefile에서는 변수를 사용하기 위해서는 값을 대입하기 위해서는 “=”을 사용하고 $(변수), ${변수}의 형식을 사용하여 값을 쓸 수 있게 된다.

 

· Makefile에서 사용되는 모든 규칙은 탭으로 시작하는 줄에 있어야 한다. 빈 칸은 허용되지 않는다. 다수의 빈 칸과 탭은 같은 것으로 보이고, 유닉스 프로그래밍의 다른 모든 경우에 빈 칸과 탭 사이에는 거의 차이가 없으므로 이것은 문제를 일으킬 수 있다. 또한, Makefile에서 줄의 마지막에 있는 빈 칸은 make명령이 실패하게 할 수 있다.

 

· 명령들을 추론하여 만들기

 

make명령은 C 소스인 경우 .o을 이용하여 .c를 추론하여 명령을 내부 규칙에 따라 gcc -c을 수행하게 된다. 아래의 List 4-5는 이것을 보여 주고 있다.

 

 

#Letting make deduce the commands

OBJECTS = main.o read.o write.o

 

test: $( OBJECTS)

       gcc -o test $( OBJECTS)

 

  main.o : io.h

 

  read.o : io.h defs.h

 

write.o : io.h buffer.h

 

.PHONY : clean

clean:

      -rm –f $( OBJECTS)

      @rm –f test

     

List 4-5. 명령을 추론하여 만든 Makefile

Note4

 

 


· 위의 clean처럼 대상(Target) 부분에 해당하는 부분이 그냥 레이블과 같이 사용될 수 있다. 이것을 Phony Target이라고 하면 위의 형식처럼 사용하는데 .PHONY는 생략하여도 된다.

 

· 위의 예제에서 rm명령 앞에 있는“-“ make가 에러를 무시하게 한다. 예를 들어, 디렉토리를 만들기 원하고 디렉토리가 이미 존재할 경우에 에러를 무시하기 원한다면, mkdir 앞에 “-“를 추가하면 된다.

 

· 위의 예제에서 rm명령 앞에 있는 @ make가 명령을 실행하는 것을 표준 출력 장치로 출력하지 않도록 지시한다.

 

 

· 또 다른 형태의 Makefile 만들기

 

내부 규칙에 따라 Target대신에 의존성(dependency)만을 사용하여 만들 것이 List 4-6이다.

 

# Alternative sytle of makefile

 

OBJECTS = main.o read.o write.o

 

test: $( OBJECTS)

       gcc -o test $( OBJECTS)

 

  $(Objects) : io.h

 

  read.o : defs.h

 

  write.o : buffer.h

 

List 4-6. 의존성을 이용하여 Makefile만들기

 

4.2. Makefile의 기능을 향상시키 주는 매크로 와 확장자 규칙

 

· 매크로(Macro)란 무엇일까?

 

매크로는 특정한 코드를 간단하게 표현하기 위해 사용되며,프로그램을 작성할 때 변수를 지정하는 것처럼 하면된다. 매크로를 Makefile에서 사용하기 위해서는 $(..) ${..}, $를 사용하는데, 일반적으로 $(..)을 사용하기를 권고한다. 아래의 List 4-7은 매크로의 사용 방법을 보여 주고 있다.

 

 

# Macro makes Makefile happy.

 

OBJECTS = main.o read.o write.o

 

test: $( OBJECTS)

       gcc -o test $( OBJECTS)

 

  $(OBJECTS) : io.h

 

  read.o : defs.h

 

  write.o : buffer.h

 

List 4-7. 간단한 매크로 예제

 

· 미리 정해져 있는 매크로(Macro)들의 무엇이 있을까?

 

프로그래머가 매크로를 만들어 사용할 수 있지만, 미리 정의된 매크로들이 있다. 이 매크로들을 보기 위해서는 make p라고 입력해 보면 make에서 미리 정의 되어 있는 모든 값들(매크로, 환경 변수등등)을 볼 수 있다. 아래의 List 4-8은 미리 정의된 매크로를 보여 주고 있으며, List 4-9는 미리 정의 된 매크로를 사용하여 만든 예제이다.

 

 

ASFLAGS = (as 명령어의 옵션 세팅)
AS = gas
CFLAGS = (gcc 의 옵션 세팅)
CC = gcc 
CPPFLAGS = (g++ 의 옵션)
CPP = g++
LDFLAGS = (ld 의 옵션 세팅)
LD = ld

List 4-8. 미리 정의된 매크로

Note5

 

 


· 매크로는 관습적으로 대문자를 작성된다.

 

· 쉘 상에서 정의한 환경 변수 값들은 그대로 이용된다.

 

 

 
OBJECTS = main.o read.o write.o
SRCS = main.c read.c write.c
 
CC = gcc
CFLAGS = -g -c
 
TARGET = test 
 
$(TARGET) : $(OBJECTS)
$(CC) -o $(TARGET) $(OBJECTS)
 
clean : 
rm -f $(OBJECTS) $(TARGET)
 
main.o : io.h main.c
read.o : io.h read.c
write.o: io.h write.c
 
$make
gcc -g -c main.c -o main.o
gcc -g -c read.c -o read.o
gcc -g -c write.c -o write.o
gcc -o test main.o read.o write.o
 
$make clean
rm -rf main.o read.o write.o test
 

List 4-9. 미리 정의된 매크로를 사용한 예제 와 실행 화면

Note6

 

 


· CFLAGS 같은 매크로는 make 파일의 내부에서 이용하여 .c파일을 .o파일로 바꾸어 주는 부분이 없어도 CFLAGS에 값을 세팅하면 make가 알아서 컴파일을 수행하여 .o파일을 생성한다.

 

 

· 확장자 규칙(Suffix rule)란 무엇인가?

 

파일의 확장자를 보고, 그에 따라 적절한 연산을 수행시키는 규칙이다. 가령 가령 .c 파일은 일반적으로 C 소스 코드를 가리키며, .o 파일은 목적 파일(Object file) 말하고 있다. 그리고 당연히 .c 파일은 컴파일되어서 .o 파일이 되어야 하는 것이다. .SUFFIXS라는 매크로를 사용하여 확장자를 등록하여 있다. 아래의 List 4-10 .SUFFIXES 매크로를 이용한 예제이다.

 

 
 .SUFFIXES : .c .o
 
 OBJECTS = main.o read.o write.o
 SRCS = main.c read.c write.c
 
 CC = gcc
 CFLAGS = -g –c
 
 TARGET = test
 
 $(TARGET) : $(OBJECTS)
      $(CC) –o $(TARGET) $(OBJECTS)
 
 clean:
      rm –f $(OBJECTS) $(TARGET)
 
 main.o : io.h main.c
 read.o : io.h defs.h read.c
 write.o : io.h buffer.h write.c
 
 
 $make
 gcc -g -c main.c -o main.o
 gcc -g -c read.c -o read.o
 gcc -g -c write.c -o write.o
 gcc -o test main.o read.o write.o
 

List 4-10. .SUFFIXES를 사용한 예제와 실행 화면

 

 

Note7

 

 


· 확장자 규칙에 의해서 make는 파일들 간의 확장자를 자동으로 인식해서 필요한 작업을 자동으로 수행한다. 위의 예제에서는 자동적으로 아래와 같은 루틴이 자동적으로 동작하게 된다.

 

.c .o :

$(CC) $(CFLAGS) -c $< -o $@

 

· make 내부에서 기본적으로 서비스를 제공해 주는 확장자는 다음과 같다.

.out .a .ln .o .c .cc .C .p .f .F .r .y .l .s .S .mod .sym .def .h .info .dvi .tex .texinfo .texi .txinfo

 

· Makefile내부에서 .SUFFIXES 매크로의 값을 세팅해 주면 내부적으로 정의된 확장자의 연산이 동작을 하게 된다. 따라서 확장자 규칙은 make가 어느 확장자를 가진 파일들을 처리할 것인가를 정해 주는 것이다.

 

 

· 내부 매크로(Internal macro)무엇일까?

 

make에서는 내부 매크로라는 것이 있다. 이것은 우리가 맘대로 정할 수 있는 매크로는 절대 아니다. 대신 매크로를 연산, 처리하는데 쓰이는 매크로라고 하는 것이 더 적당할 것이다. 아래의 Table 4-1은 내부 매크로에 대해 설명하고 있으며, List 4-11은 내부 매크로를 사용한 예를 보여 주고 있다.

 

내부 매크로

         

$*

확장자가 없는 현재의 대상(Target) 파일

$@

현재의 대상(Target) 파일

$<

현재의 대상(Target) 파일보다 더 최근에 갱신된 파일 이름

(dependency중에서 처음에 위치한 파일 이름)

$?

현재의 대상(Target) 파일보다 더 최근에 갱신된 파일 이름

(모든 dependency 파일의 이름)

Table 4-1. 내부 매크로

 

 
 main.o : main.c io.h
     gcc -c $*.c ($* 는 확장자가 없는 현재의 대상 파일이므로 $* 는 결국 main에 해당된다.)
 
 test : $(OBJECTS)
     gcc –o $@ $*.c ($@ 는 현재의 대상 파일이므로 $* 는 결국 test에 해당된다.)
 
 .c .o :
     gcc -c $< (또는 gcc -c $*.c)
($< 는 현재의 대상 파일보다 더 최근에 갱싱된 파일 이름이므로, .o 파일보다 더 최근에 갱신된 .c 파일은 자동적으로 컴파일이 된다. 가령 main.o를 만들고 난 다음에 main.c를 갱신하게 되면 main.c $<의 작용에 의해 새롭게 컴파일 된다.)

List 4-11은 내부 매크로를 사용한 예제

4.3 Makefile를 작성할 때 알면 좋은 것들

 

· 긴 명령어를 여러 라인으로 표시하기

 

Makefile을 작성할 때 명령어가 한 줄을 넘어간다면, \문자를 이용해서 여러 라인으로 나타낼 수 있다. 아래의 List 4-12은 긴 명려어를 여러 라인으로 나타내는 예제이다.

 

 

OBJECTS = shape.o \

          rectangle.o \

          circle.o \

          line.o \

          main.o \

          read.o \

          write.o \

List 4-12. 여러 라인으로 명령어 나타내는 예제

 

· 매크로 치환(Macro substitution)

 

매크로를 지정하고, 그것을 이용하는 것을 이미 알고 있다. 그런데 필용에 의해 매크로의 내용을 조그만 바꾸어야 할 때가 있다. 매크로 내용의 일부만 바꾸기 위해서는 $(MACRO_NAME:OLD=NEW)과 같은 형식을 이용하면 된다. 아래의 List 4-13은 매크로 치환에 대한 예제이다.

 

 

MY_NAME = Hello World

YOUR_NAME = $(MY_NAME:Hello=Hi)

 

(Jack이란 부분이 Jook으로 바뀌게 된다. YOUR_NAME이란 매크로의 값은 Hi World이 된다.)

 

 

OBJS = main.o read.o write.o

SRCS = $(OBJS:.o=.c)

 

(SRCS에서는 OBJS에서 .o .c로 바뀌게 된다. 다음과 같이 변경되는 것이다.

SRCS = main.c read.c write.c)

List 4-13. 매크로 치환에 대한 예제

 

· 자동 의존 관계 생성(Automatic dependency)

 

일반적인 make 구조는 대상(target), 의존 관계(dependency), 명령(command)이 연속적으로 정의 되어 있는데 실행되는데, 명령이 없이 대상과 의존 관계만 표시가 되면 이는 대상이 어는 파일에 의존하고 있는지 표시해 주는 정보 역할을 하게 됩니다. Makefile을 작성할 때 없어서는 안되는 부분입니다. 그런데 일일이 이런 정보를 만든다는 것은 쉬운 일이 아닙니다. 이런 단조롭고 귀찮은 일을 자동으로 해주기 위해서 gcc M XX.c의 형식을 사용하여 의존 관계를 만들게 된다. 프로그램을 설치할 때 make dep라는 것을 친 기억이 있을 것이다. 파일들의 의존 관계를 작성해 준다는 의미이다. List 4-14는 자동 의존 관계를 생성하는 예제이다.

 

 

.SUFFIXES : .c .o 
CFLAGS = -O2 -g
 
OBJS = main.o read.o write.o 
SRCS = $(OBJS:.o=.c)
 
test : $(OBJS)
          $(CC) -o test $(OBJS)
 
dep :
$(CC) –M $(SRCS)
 
 $make dep
 $vi Makefile
 
main.o: main.c /usr/include/stdio.h /usr/include/features.h \
/usr/include/sys/cdefs.h /usr/include/libio.h \
/usr/include/_G_config.h io.h
read.o: read.c io.h defs.h
write.o: write.c io.h buffer.h
  (Makefile의 뒷부분에 위에 내용이 첨가 된다.)

 

List 4-14. 자동 의존 관계 생성 예제 및 실행화면

 

 

 

 

 

 

 

 

 

 

 

 

 

· 다중 타켓(Multiple Target)

 

하나의 Makefile에서 꼭 하나의 결과만 만들어 내라는 법은 없다. 결과 파일이 3개가 필요한 경우도 있다. 아래의 List 4-15는 다중 대상(Target)에 대한 예제이다.

 

 

.SUFFIXES : .c .o 
CC = gcc
CFLAGS = -O2 -g
 
OBJS1 = main.o test1.o
OBJS2 = main.o test2.o 
OBJS3 = main.o test3.o 
SRCS = $(OBJS1:.o=.c) $(OBJS2:.o=.c) $(OBJS3:.o=.c) 
 
all : test1 test2 test3
 
test1 : $(OBJS1)
        $(CC) -o test1 $(OBJS1) 
 
test2 : $(OBJS2)
        $(CC) -o test2 $(OBJS2)
 
test3 : $(OBJS3)
        $(CC) -o test3 $(OBJS3)
 
deep :
        $(CC) -M $(SRCS)
 
$make all (또는 make)
gcc -O2 -g -c main.c -o main.o
gcc -O2 -g -c test1.c -o test1.o
gcc -o test1 main.o test1.o ( test1 의 생성 ) 
gcc -O2 -g -c test2.c -o test2.o
gcc -o test2 main.o test2.o ( test2 의 생성 )
gcc -O2 -g -c test3.c -o test3.o
gcc -o test3 main.o test3.o ( test3 의 생성 )
 

 

List 4-15. 다중 대상(Target)에 대한 예제 및 실행 화면.

 

 

 

 

· 순환 make(Recursive make)

 

규모가 큰 프로그램들은 파일들이 하나의 디렉토리에 있지 않는 경우가 많다. 여러 개의 서브시스템이 전체 시스템을 구성한다고 가정하면 각 서브시스템에 Makefile이 존재한다. (서브시스템 = 서브디렉토리) 따라서 여러 개의 Makefile을 동작시킬 필요가 있도록 Makefile을 고쳐 보자. 서브디렉토리에 있는 Makefile을 동작시키는 방법은 의외로 간단하다. 아래의 List 4-16은 순환 make에 대한 예를 보여 주고 있다.

 

subsystem:
                cd subdir; $(MAKE) .....(1)
 
subsystem:
                $(MAKE) -C subdir .....(2)

 

(위의 예제에서 (1) (2)는 동일한 명령을 수행한다 우리가 만들 시스템의 타겟이 subsystem이다. 우선 subdir이라는 곳으로 가서, 거기에 있는 Makefile을 동작시키게 된다. MAKE라는 것은 그냥 make라는 명령어를 표시하는 매크로일 뿐이다.)

 

SUFFIXES : .c .o
CC = gcc
CFLAGS = -O2 -g
 
all : DataBase Test <- 요기에 집중.
 
DataBase:
    cd db ; $(MAKE)  (db 로 이동해서 make 실행 )
 
Test: 
    cd test ; $(Make)  ( db 로 이동해서 make 실행 )

 

$make
cd db ; make
make[1]: Entering directory`/home/raxis/TEST/src'
gcc -O2 -g -c DBopen.c -o DBopen.o
gcc -O2 -g -c DBread.c -o DBread.o
gcc -O2 -g -c DBwrite.c -o DBwrite.o
make[1]: Leaving directory `/home/windows/TEST/src'
cd test ; make
make[1]: Entering directory `/home/raxis/TEST/test'
gcc -O2 -g -c test.c -o test.o
make[1]: Leaving directory `/home/windows/TEST/test'

(make뒤의 대괄호의 1이라고 나타난 것은 현재의 레벨을 의미한다. 원래 디렉토리의 레벨이 0이고, 여기서는 레벨이 하나 더 내려갔으므로 1이라고 표시된 것이다.)

List 4-16. 순환 make 대한 예제 및 실행 화면

· 불필요한 재컴파일 막기

 

의존 관계 규칙에 의해 하나가 바뀌면 그에 영향받는 모든 파일이 바뀐다고 앞에서 말했다. 그러나 다른 파일들에게 아무 영향을 주지 않도록 수정하였는데도 재컴파일을 시도한다면 시간 낭비가 될 수도 있다. 가령 모든 .c 파일에서 include 하는 헤더 파일에서 새로운 #define PI 3.14 라고 정의를 했다고 가정하자. 그리고 PI라는 것은 아무 곳에서도 사용을 하지 않는다. 이때는 'make -t' 라고 해보자. -t touch를 의미하는 옵션으로써 컴파일을 하지 않는 대신 파일의 생성 날짜만 가장 최근으로 바꾸어 놓는다. 새로 컴파일 된 것처럼 처리를 하는 것이다. touch유틸리티 명렁어에 익숙한 사람이라면 이해할 것이다. touch는 파일의 생성 날짜를 현재로 바꾸어 주는 간단한 유틸리티이다.

4.4 Makefile에 사용되는 옵션

make에서 거의 모든 것은 Makefile내부에서 모두 지정을 할 수 있다. 그중 일부를 make의 실행 시에 옵션으로 통해서 줄 수도 있다. List 4-17은 수많은 옵션중에서 가장 많이 사용 옵션을 보여 주고 있다.

-C dir

위에서도 밝혔듯이 Makefile을 계속 읽지 말고 우선은 dir로 이동하라는 것이다. 순환 make에 사용된다.

 

-d

Makefile을 수행하면서 각종 정보를 모조리 출력해 준다. (-debug) 결과를 파일로 저장해서 읽어보면 make 의 동작을 대충 이해할 수 있다.

 

-h  옵션에 관한 도움말을 출력한다. (-help)

 

-f file  file 에 해당하는 파일을 Makefile로써 취급한다. (-file)

 

-r

내장하고 있는 각종 규칙(Suffix rule )을 없는 것으로 (-no-builtin-rules)간주한다. 따라서 사용자가 규칙을 새롭게 정의해 주어야 한다.

 

-t  파일의 생성 날짜를 현재 시간으로 갱신한다. (-touch)

 

-v  make의 버전을 출력한다. ( GNU make 3.73 을 씁니다.) (-version)

 

-p  make에서 내부적으로 세팅되어 있는 값들을 출력한다. (-print-data-base)

 

-k  

make는 에러가 발생하면 도중에 실행을 포기하게 되는데 (-keep-going) -k 는 에러가 나더라도 멈추지 말고 계속 진행하라는 뜻.

List 4-17. Makefile의 옵션들

4.5 Makefile의 실제 예제

아래의 List 4-18 List 4-19 Makefile 예를 보여 주고 있다.

 

HOSTARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \

           -e s/arm.*/arm/ -e s/sa110/arm/ -e s/macppc/ppc/)

 

HOSTOS := $(shell uname -s | tr A-Z a-z)

 

ifeq ($(HOSTARCH),ppc)

CROSS_COMPILE =

else

CROSS_COMPILE =

endif

 

export CROSS_COMPILE HOSTARCH

 

TOPDIR := $(shell if [ "$$PWD" != "" ]; then echo $$PWD; else pwd; fi)

 

export TOPDIR

 

include $(TOPDIR)/config.mk

          

SUBDIRS = common driver

 

#OBJS = ascu/libascu.a

OBJS += driver/libdriver.a

#OBJS += net/libnet.a

OBJS +=        common/libcommon.a

 

all:   ascu

       @for dir in $(SUBDIRS); \

       do \

               $(MAKE) -C $$dir || exit 1 ; \

       done

 

ascu:  depend subdirs $(OBJS) $(LDSCRIPT)

               $(CC) -o ascu_prog $(OBJS)  -D_REENTRANT -lpthread

 

subdirs:

               @for dir in $(SUBDIRS) ; \

               do \

                        $(MAKE) -C $$dir || exit 1 ; \

               done

 

depend dep:

               @for dir in $(SUBDIRS) ; \

               do \

                        $(MAKE) -C $$dir .depend ; \

               done    

 

clean:

       rm -f `find . -type f \

               \( -name '*.o' -o -name '*.a' \) -print`

       rm       -f ascu_prog ascu.elf ascu.map

 

clobber:       clean

       rm -f `find . -type f \

               \( -name .depend -name '*.o' -o -name '*.a' \) \

               -print`

       rm -f ascu_prog ascu.elf ascu.map

List 4-18  첫번째 Makefile 예제

 

include $(TOPDIR)/config.mk

 

LIB = ascu

 

LIBDIR = lib$(LIB).a

 

OBJS   = $(patsubst %.c,%.o,$(wildcard *.c))

 

$(LIBDIR):     .depend $(OBJS)

       $(AR) crv $@ $^

 

#########################################################################

 

.depend:       Makefile $(SOBJS:.o=.S) $(OBJS:.o=.c)

               $(CC) -M $(CFLAGS) $(SOBJS:.o=.S) $(OBJS:.o=.c) > $@

 

sinclude .depend

 

#########################################################################

 

List 4-19  두번째 Makefile 예제

아래의 List 4-20 List 4-19 Makefile에 의해 생성된 dependency 파일을 보여 주고 있다.

 

 

main.o: main.c /usr/include/stdio.h /usr/include/features.h \

 /usr/include/sys/cdefs.h /usr/include/gnu/stubs.h \

 /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stddef.h \

 /usr/lib/gcc-lib/i386-redhat-linux/egcs-2.91.66/include/stdarg.h \

 /usr/include/bits/types.h /usr/include/libio.h \

 /usr/include/_G_config.h /usr/include/bits/stdio_lim.h \

 /usr/include/bits/stdio.h

List 4-20  생성된 dependency 파일(.depend)

 

전처리기(펌)

#include 전처리기 지시어

ex)

#include<filename> //표준 라이브러리 헤더 파일

#include"filename" //사용자 정의 헤더파일


#define 전처리기 지시어 : 기호 상수

ex)

#define 식별자  치환텍스트

#define PI 3.14159


#define 전처리기 지시어 : 매크로

ex)

#define CIRCLE_AREA(x) (PI*(x)*(x))


조건부 컴파일

#ifndef NULL //널이 정의 되지 않았다면

    #define NULL 0 //NULL을 0으로 정의

#endif //조건 디파인 종료

ex)

#if 0

    컴파일이 되지 않는 코드

#endif //주석처럼 사용 가능


#error 지시어

는 지시어에 지정된 토큰들을 포함한 시스템 구현에 의존적 메시지를 인쇄한다

ex)

#error 1 - Out of range error //#error지시어가 처리 될때 지시어에 있는 토큰들이 오류메시지로

                                        //표시되고 전처리가 중단되고, 프로그램은 컴파일 되지 않는다

#pragma 지시어

는 구현에 정의된 동작을 한다


#연산자

는 치환 텍스트 토큰을 큰따옴표로 둘러싸인 문자열로 변환

ex)

#define HELLOR(x) cout<<"Hello, " #x<<endl;

HELLO(John)은 cout<<"Hello, " "John"<<endl;로 치환됨

##연산

기호는 두개의 토큰을 연결한다

ex)

#define TOKENCONCAT(x, y) x ## y

TOKENCONCAT(O, K)는 프로그램에서 OK로 치환됨


#line 전처리기 지시어

는 뒤이어 나오는 소스 코드 줄번호를 지정된 상수 정수 값에서 시작하여 번호가 매겨지도록 함

ex)

#line 100 //다음 소스코드 줄 번호를 100부터 시작하도록


미리 정의된 기호 상수

_LINE_    현재 소스 코드 줄의 줄번호

_FILE_    소스 파일의 추정되는 이름

_DATA_    소스 파일이 컴파일된 날짜

_STDC_    프로그램이 ANSI C표준을 따르는지

_TIME_    소스 파일이 컴파일된 시간

_TIMESTAMP_    소스 파일의 최종 변경 날짜와 시간

zlib, openssl, openssh 크로스컴파일(arm용)

참조:http://webnautes.tistory.com/194

zlib

localhost opt # wget http://www.gzip.org/zlib/zlib-1.2.3.tar.gz

localhost opt # tar xvzf zlib-1.2.3.tar.gz

localhost opt # cd zlib-1.2.3

localhost zlib-1.2.3 # CC=arm-linux-gcc AR="arm-linux-ar rc" RANLIB=arm-linux-ranlib ./configure --shared --prefix=$PWD/build

localhost zlib-1.2.3 # make && make install


openssl

다운로드 http://www.openssl.org/source/

localhost opt # wget http://www.openssl.org/source/openssl-0.9.8e.tar.gz

localhost opt # tar xvzf openssl-0.9.7a.tar.gz

localhost opt # cd openssl-0.9.7a

localhost openssl-0.9.7a #vi Configure
Configure파일 수정
 "linux-elf" "gcc, ...................." <- 이부분을 찾아 다음처럼 수정한다.
=> "linux-elf-arm" "arm-linux-gcc,............." 변경후 아래를 실행한다.

localhost openssl-0.9.7a #./Configure linux-elf-arm --prefix=/usr/local/openssl-arm --openssldir=/usr/local/openssl-arm -L/opt/zlib-1.2.3/build/lib shared no-threads no-asm

Make File 수정
-CC= gcc
+CC= arm-linux-gcc
-EX_LIBS=
+EX_LIBS= -ldl
-AR=ar $(ARFLAGS) r
-RANLIB= /usr/bin/ranlib
+AR=arm-linux-ar $(ARFLAGS) r
+RANLIB= arm-linux-ranlib

make
make install



openssh

localhost opt # wget ftp://ftp.iij.ad.jp/pub/OpenBSD/OpenSSH/portable/openssh-4.6p1.tar.gz

localhost opt # tar xvzf openssh-4.6p1.tar.gz

localhost opt # cd openssh-4.6p1

localhost openssh-4.6p1 # CC=arm-linux-gcc AR=arm-linux-ar ./configure --prefix=/nfsroot/openssh --sysconfdir=/nfsroot/openssh/etc/ssh --target=arm-linux --host=arm-linux --with-ldflags="-static-libgcc" --with-zlib=/opt/zlib-1.2.3/build --disable-etc-default-login --disable-lastlog  --with-ssl-dir=/usr/local/openssl-arm --with-libs="-L/usr/local/openssl-arm/lib" --disable-strip  --without-pam --with-pid-dir=/nfsroot/openssh --with-privsep-path=/nfsroot/openssh/empty

make
make install


make install 실행 도중에 아래와 같은 에러가 난다. arm용으로 컴파일 된 실행파일을  PC에서 실행시키려 했기 때문이다.

/bin/sh: line 4: ./ssh-keygen: cannot execute binary file
/bin/sh: line 9: ./ssh-keygen: cannot execute binary file
/bin/sh: line 14: ./ssh-keygen: cannot execute binary file
make: *** [host-key] 오류 126


make파일을 arm용으로 컴파일 하여 계속 진행하였다.

타겟보드에서 make host-key라고 명령을 내린다.
./ssh-keygen: error while loading shared libraries: libz.so.1: cannot open shared object file: No such file or directory
./ssh-keygen: error while loading shared libraries: libz.so.1: cannot open shared object file: No such file or directory
./ssh-keygen: error while loading shared libraries: libz.so.1: cannot open shared object file: No such file or directory
make: *** [host-key] Error 127

기존에 컴파일 해두었던 libz.so.1을 타겟보드의 /lib/로 복사한후 계속 진행한다.
localhost lib # pwd
/opt/zlib-1.2.3/build/lib
localhost lib # cp -a * /nfsroot/
localhost lib # ls -l /nfsroot/

타겟보드에서
# cp -a /nfsroot/libz.so* /lib/


make host-key
에러가 나면 현재시간을 다시 맞추어주자..
make: warning:  Clock skew detected.  Your build may be incomplete.

# date
Thu Jan  1 01:08:41 UTC 1970
# date 062911382007   
Fri Jun 29 11:38:00 UTC 2007


ssh_conf파일을 수정해야 한다. 옵션설정은 더봐야함
[root@acumen:ssh]# pwd
/nfsroot/openssh/etc/ssh
[root@acumen:ssh]# vim ssh_config

38줄 SSH1 또는 SSH2 사용여부..#을 제거한다.
#   Protocol 2,1 -> Protocol 2,1


타겟 보드에서 sshd를 실행시킨다.
/nfsroot/openssh/sbin/sshd


PC에서 접속을 해본다.
ssh 192.168.1.166

[펌] 프린터포트 관련자료

참조 : http://www.whiteat.com/zbxe/4848



기본 지식

 

1. 병렬포트의 구조

프린트포트01.JPG
                    
그림 1 프린터 포트의 외형도



1, 14, 16, 17번은 input도 되고 output도 된다는 것을 알 수 있다.





그래서 여기서는 1, 14, 16, 17번은 사용을 안 하기로 한다.


OUTPUT

2, 3, 4, 5, 6, 7, 8, 9

INPUT

10, 11, 12, 13, 15

IN/OUT

1, 14, 16, 17

GND

18, 19. 20, 21, 22, 23, 24, 25

                           1 프린터 포트의 핀 종류



2. 병렬포트의 출력

병렬 테스트를 하기 위해 간단히 회로를 꾸며 보자.  아래의 회로도처럼 꾸민다.

프린트포트02.JPG

그림 2 프린터 포트 입력 테스트를 위한 회로도

회로도가 완성되면, PC 쪽에서 프린터 포트를 제어 해야 한다.

VC++를 사용해서 제어 하겠다. (C 보다는 VC++ 이 편하다.)

Dialog Based 로 한 다음, 버튼을 하나 만들고, 그 버튼 메시지 아래와 같은 코드를 입력

for(int i=0;i<256;i++)  {

_outp(0x378,i);

for(int j=0;j<10000;j++);

} 

그리고 버튼을 누르면  병렬포트에 연결시켰던 LED들이 우르르~ 켜지는 것을 볼 수 있다.

(하지만, win2000 이상에서는 잘 안될 것이다.)

그래서 다른 방법을 써야 한다.  라이브러리를 사용해야 한다.

라이브러리를 사용하면  2000 이하의 버전에서도 사용이 가능하므로, 라이브러리를 사용해서 하는게 좋을 것이다. (나중에 자세히 설명한다.)

 

파일은 자료실에 있다.

     _outp(0x378,i); 함수에서,

0x378 LPT1일때 즉 프린터포트의 주소이고,  그 뒤의 값은 데이터 값이다.

, 2번부터 9번까지(Data0 부터 Data7까지)데이터를 출력해 준다.

예를들어,  _outp(0x378,9)라고 했다면,
9 2진수로 1001이므로 2번핀과 5번핀이 HIGH 로 된다.



cf. 라이브러리를 이용한 제어

http://www.driverlinx.com/ 에 자세한 설명이 있고,

라이브러리를 제작, 배포하는 곳이다. 다운 받으려면 가입을 해야 하지만

http://www.driverlinx.com/DownLoad/DlPortIO.htm 여기에서 그냥 받을 수 있다.

또는 자료실 (http://rtcontrol00.ee.hallym.ac.kr ) 에 있다.

 

port95nt.exe Port I/O Driver (1,573k)

를 다운 받아 설치한다. 다 끝나면 (꼭 리붓을 해야 한다.)

프린트포트03.JPG

그림 3 생성된 단축 아이콘



그림의 C++ PortIO 를 실행하면

프린트포트04.JPG    프린트포트05.JPG                  

그림 4 실행초기 화면                   

그림 5 프린터 포트를 쓰는 화면

프린터 포트를 쓰는 그림에서 Write 를 누르면 프린터 포트에 데이터를 쓰는 것이다.

0x378 은 프린터 포트를 쓰는 주소이고, 0은 데이터 이다.

즉 프린터 포트에 0을 쓰는 것이다.

값을 읽을 때에는 Read 를 누르면 된다.

일단 여기서 에러 없이 잘 되면, 프린터 포트 제어에 문제가 없는 것이다.

이제 자신의 코드를 만들어 보자. VC6.0++ 을 이용하겠다.


VC++로 라이브러리 추가하여 프로그램 짜기

다이러로그 방식으로 프로그램을 짜서 버튼을 만들고 메시지를 연결한다.

라이브러리를 연결해야 하는데,

우선, 라이브러리를 사용할 폴더로 복사한다. [ Dlportio.h 파일과 Dlportio.lib 파일 ]

프린트포트06.JPG

설치된 폴더에서 API 폴더에 있다.

 

그리고, 라이브러리를 연결하고, 헤더파일을 추가한다.

(VC++ 에 대한 설명은 생략한다.)

 

그리고 코드를 연결한다.

 

void CPTESTDlg::OnButton1() {

             for(int i=0;i<256;i++)  {

                           DlPortWritePortUchar(0x378,i);

                           Sleep(1);

             }           

}DlPortWritePortUchar() 함수는 포트에 값을 쓰는 함수이다.

함수를 알아보려면 Dlportio.h 를 열어 보기 바란다.(절대 수정하지는 말고 ^^;; )

 

그리고 버튼을 누르면 LED 가 빠르게 깜빡거릴 것이다.


dlportio.h 파일을 보면 다음과 같은 함수가 있다.

<Read 함수>
DlPortReadPortUchar(IN ULONG Port);
DlPortReadPortUshort(IN ULONG Port);
DlPortReadPortUlong(IN ULONG Port);
DlPortReadPortBufferUchar(IN ULONG Port,IN PUCHAR Buffer,IN ULONG Count);
DlPortReadPortBufferUshort(IN ULONG Port,IN PUSHORT Buffer,IN ULONG Count);
DlPortReadPortBufferUlong(IN ULONG Port,IN PULONG Buffer,IN ULONG Count);

<Write 함수>
DlPortWritePortUchar(IN ULONG Port,IN UCHAR Value);
DlPortWritePortUshort(IN ULONG Port,IN USHORT Value);
DlPortWritePortUlong(IN ULONG Port,IN ULONG Value);
DlPortWritePortBufferUchar(IN ULONG Port,IN PUCHAR Buffer,IN ULONG Count);
DlPortWritePortBufferUshort(IN ULONG Port,IN PUSHORT Buffer,IN ULONG Count);
DlPortWritePortBufferUlong(IN ULONG Port,IN PULONG Buffer,IN ULONG Count);

 

뒤에 붙은 char(1byte), short(2byte), long(4byte) 은 데이타 타입이다.

자주 사용하는 함수는  DlPortReadPortUchar(), DlPortWritePortUchar() 이고, 바이트 단위로 통신하는 것이다
.
 

이제부터 라이브러리를 사용한다고 가정하고 설명하겠다.



3. 병렬포트의 입력

parallel port를 통해 입력을 받아보기 위해 회로를 꾸미자.

 

프린트포트07.JPG

그림 6 입력과 출력 회로도

주의 사항

최상위 비트는 11번이다.

11,10,12,13,15번 순이다. 주의 요

 

입력 버튼을 만들고, 코드를 넣자.

void CPTESTDlg::OnButton2() {

             int a=DlPortReadPortUchar(0x379);

             m_strData.Format("%x",a);

             UpdateData(FALSE);

}

 

테스트를 해 보면 11(최상위) 핀은 반전되어서 나오는 것을 알수 있다.

( 뒤쪽의 3비트는 I/O 공동으로 사용한 데이터 같은데 뭔지 모르겠음)

입력 값

PC 에서 읽은 값

1111 1XXX

0111 1XXX

0000 0XXX

1000 0XXX

1010 1XXX

0010 1XXX

0110 0XXX

1110 0XXX



실시간으로 프린트 포트로부터 입력 받기

약간의 C++ 지식이 필요하다.

일단 소스를 보면

void CPTESTDlg::OnButton2() {

        // 실시간으로 프린터 포트의 입력 값을 읽어서 화면에 보여줌

             while(1){

                           MSG msg;

                           while(::PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)){

                                        switch(msg.message){

                                        case WM_QUIT:

                                        case WM_DESTROY:

                                        case WM_CLOSE:

                                                     PostQuitMessage(0);

                                                     return ;

                                        }

                                        ::TranslateMessage(&msg);

                                        ::DispatchMessage(&msg);

                           }

                          

                           int a=DlPortReadPortUchar(0x379);

                           m_strData.Format("%x",a);

                           UpdateData(FALSE);

             }

}

그리고 종료 할 때,

PostQuitMessage(0);

를 꼭 해주어야 한다. 그렇지 않으면 리소스를 반납하지 않기 때문에, PC 가 느려진다.

 

꼭 리소스가 제대로 반납되는지 확인 하기 바란다.




<참고> ECP , EPP 모드에 관해

하이텔 :신동익  (kbread  )님께서 쓰신 글입니다.

안녕하세요.
프린터포트로 뭔가를 하려는 분이 많이 계신가본데,제가 얼마 전에 인터넷에서
자료를 좀 구했습니다.

보통 centronics라고 불리우는 일반적인 프린터 포트 방식으로는 4bit의 데이타를 그것도 출력으로만 사용할 수 있지요.

최근에는 외장 하드디스크나 zip drive 등 대용량, 고속으로 동작하는 외부기기를 프린터포트에 접속하여 사용하는 경우가 늘고 있습니다. 따라서 병렬포트(프린터 포트보다 치기가 쉬워서)도 기존의 규격으로는 부족한 감이 있지요.

그래서 기존의 모드를 그대로 지원하면서, 고속의 확장모드를 지원할 수 있도록  새로운 규격의 병렬포트가 등장한 겁니다. 이른바 EPP, ECP라는 것 말입니다.

EPP Enhanced Parallel Port의 약자로서 프린터를 제외한 고속, 대용량(외장 하드, 캐너 등)의 외부기기를 지원하는 모드입니다. 8bit의 데이타 버스를 양방향으로 이용할 수 있습니다.

ECP Extended Compatibility Port의 약자로서 프린터로의 데이타 전송을 고속화 하려는 목적으로 고안된 것입니다. 마찬가지로 8bit의 양방향 통신을 할 수 있습니다. 최근의 레이저 프린터들은 대개 이 모드를 지원합니다.

또한 이러한 모드를 이용하기 위해서는 PC에 이 모드를 지원하는 병렬포트가 장착되어 있어야 하는데, PC의 부팅 시 cmos setup에 들어가서 지원여부를 확인할 수 있습니다. setup menu에서 communication port쪽을 살펴보면, com1은 어쩌구, com2는 저쩌구, lpt1은 어쩌구 에서 LPT부분의 메뉴를 변경해 보면 normal, ECP, EPP, ECP+EPP등이 나타나면 이게 지원되는 보드입니다.

ECP 모드에서의 전송 속도는 500Kbyte/sec에서 최대 2MByte/sec까지 가능합니다.
이 정도의 속도라면 보통 ISA bus에 인터페이스 한 것과 거의 대등한 속도라고 봅니다. 그리고 프로토콜도 매우 간단해서 프린터포트 인터페이스를 해 보신 분이라 면 누구나 쉽게 할 수 있습니다.

보다 자세한 규격은 인터넷주소 http://www.fapo.com/ieee1284.htm에 있습니다.
참고로 ECP, EPP를 지원하는 병렬포트 규격을 IEEE1284라 합니다.

MDI/MDI-X 란?

MDI(Medium Dependent Interface)와 MDIX(MDI Crossover)
 크로스 연결을 해야할때 크로스케이블이 없고, 스트레이트(or 다이렉트)케이블만 있을경우
 MDI/MDI-X 표시가 있는 제품의 스위치나, 허브는 자동으로 케이블을 확인하고, 연결을 해준다.
 특히 요즘 나오는 제품들은 거의 이렇다고 보면 되기때문에 케이블 때문에 문제가 생기는 경우는 없다
 고 보면 된다.
 
 - 좋은 세상이다...^^

(펌)포트번호 정리

퍼온 사이트(http://kin.naver.com/open100/db_detail.php?d1id=1&dir_id=106&eid=kIZ5a6tTl/UvwmoBIGIC37tlw7HFaHrL&qb=aWNtcCBwb3J0)

▶ TCP/IP 프로토콜에서 사용하는 Port

1) 가장 많이 사용되는 포트 : 0 ~ 1023

2) 예약된 포트 : 1024 ~ 49151

3) 동적, 사설 포트 : 49152 ~ 65535


▶ 가장 많이 사용되는 포트 요약  (0~1023)

키워드

포트번호

포트용도

icmp

8/tcp, 8/udp,

0/tcp, 0/udp

Unassigned

ftp-data

20/tcp, 20/udp

File Transfer [Default Data]

ftp

21/tcp, 21/udp

File Transfer [Control]

ssh

22/tcp, 22/udp

SSH Remote Login Protocol

telnet

23/tcp, 23/udp

Telnet

smtp

25/tcp, 25/udp

Simple Mail Transfer

domain

53/tcp, 53/udp

Domain Name Server

whois++

63/tcp, 63/udp

whois++

tftp

69/tcp, 69/udp

Trivial File Transfer

gopher

70/tcp, 70/udp

Gopher

finger

79/tcp, 79/udp

Finger

www

80/tcp, 80/udp

World Wide Web HTTP

pop3

110/tcp, 110/udp

Post Office Protocol - Version 3

ntp

123/tcp, 123/udp

Network Time Protocol

epmap

135/tcp, 135/udp

DCE endpoint resolution

profile

136/tcp, 136/udp

PROFILE Naming System

netbios-ns

137/tcp, 137/udp

NETBIOS Name Service

netbios-dgm

138/tcp, 138/udp

NETBIOS Datagram Service

netbios-ssn

139/tcp, 139/udp

NETBIOS Session Service

imap

143/tcp, 143/udp

Internet Message Access Protocol

snmp

161/tcp, 161/udp

SNMP

namp

167/tcp, 167/udp

NAMP

imap3

220/tcp, 220/udp

Interactive Mail Access Protocol v3

ldap

389/tcp, 389/udp

Lightweight Directory Access Protocol

https

443/tcp, 443/udp

http protocol over TLS/SSL

shell

514/tcp

cmd

syslog

514/udp

syslog

printer

515/tcp, 515/udp

spooler

ftps-data

989/tcp, 989/udp

ftp protocol, data, over TLS/SSL

ftps

990/tcp, 990/udp

ftp protocol, control, over TLS/SSL

telnets

992/tcp, 992/udp

telnet protocol over TLS/SSL

imaps

993/tcp, 993/udp

imap4 protocol over TLS/SSL

pop3s

995/tcp, 995/udp

pop3 protocol over TLS/SSL (was spop3)

 

▶ 예약된 포트 요약  (1024 ~ 49151)

키워드

포트번호

포트용도

ms-sql-s

1433/tcp, 1433/udp

Microsoft-SQL-Server

ms-sql-m

1434/tcp, 1434/udp

Microsoft-SQL-Monitor

sybase-sqlany

1498/tcp, 1498/udp

Sybase SQL Any

atm-zip-office

1520/tcp, 1520/udp

atm zip office

ncube-lm

1521/tcp, 1521/udp

nCube License Manager

ricardo-lm

1522/tcp, 1522/udp

Ricardo North America License Manager

cichild-lm

1523/tcp, 1523/udp

cichild

ingreslock

1524/tcp, 1524/udp

ingres

orasrv

1525/tcp, 1525/udp

oracle

sybasedbsynch

2439/tcp, 2439/udp

SybaseDBSynch

sybaseanywhere

2638/tcp, 2638/udp

Sybase Anywhere

ms-wbt-server

3389/tcp, 3389/udp

MS WBT Server

http-alt

8080/tcp, 8080/udp

HTTP Alternate (see port 80)


▶ 웜/바이러스 포트로 방화벽에서 차단해야할 포트 요약

포트번호

원래 포트 용도

키워드

69/udp

TFTP

Nachi 웜,

Blaster 웜

80/udp

web server

Nachi 웜

135/tcp, 135/udp

NETBios

Nachi 웜,

Blaster 웜

137/udp

NETBios

Nachi 웜,

Blaster 웜

138/udp

NETBios

Nachi 웜,

Blaster 웜

139/tcp

NETBios

Nachi 웜,

Blaster 웜

443/tcp, 443/udp

HTTPS

Slapper 웜

445/tcp

NETBios

Nachi 웜,

Blaster 웜

514/tcp

SHELL

RPC Backdoor

515/tcp, 515/udp

LPRng

Red 웜

593/tcp

http-rpc-epmap, HTTP RPC Ep Map

Nachi 웜,

Blaster 웜

1008/udp

-

LiOn 웜

1243/tcp

-

ShoolBus Backdoor

1433/tcp, 1433/udp

ms-sql-m, Microsoft-SQL-Monitor

W32.Slammer 웜

1434/tcp, 1434/udp

ms-sql-m, Microsoft-SQL-Monitor

W32.Slammer 웜

3385/tcp

qnxnetman

Net-Worm.Win32.Mytob.dc

4444/tcp

krb524

Blaster 웜,

Welchia 웜

6667/tcp, 6667/udp

ircu 6665-6669/tcp  IRCU

Welchia 웜

6668/tcp, 6668/udp

ircu 6665-6669/tcp  IRCU

Welchia 웜

6669/tcp, 6669/udp

ircu 6665-6669/tcp  IRCU

Welchia 웜

10008/tcp, 10008/udp

-

LiOn 웜

54321/tcp

-

ShoolBus Backdoor

17300/tcp

-

Kuang2 바이러스

30999/tcp

-

Kuang2 바이러스

27374/tcp, 27374/udp

-

SubSeven Backdoor

 

▶ 메신저 관련

Service

Name

Server

Port

Description

MSN

64.4.130.0/24

207.46.104.0/24

207.46.106.0/24

207.46.107.0/24

207.46.108.0/24

207.46.110.0/24

TCP 1863 ,80

1863접속 시도 후 차단 되면 80 접속 시도

TCP 6891-6900

파일 전송

UDP 6901

음성채팅

UDP1863,5190

Microsoft Network Messenger

Yahoo

216.136.233.152/32

216.136.233.153/32

216.136.175.144/32

216.136.224.143/32

66.163.173.203/32

216.136.233.133/32

216.136.233.148/32

66.163.173.201/32

216.136.224.213/32

TCP 5050,5101

5050 접속 시도 후 차단 되어 있으면Port 계속 변경

TCP 5000-5001

음성채팅

TCP 5100

화상채팅

Nate On

203.226.253.75/32

203.226.253.135/32

203.226.253.82/32

TCP 5004-5010

기본 포트 5004-5010 접속 시도후 차단되어 있으면 Port를 계속 변경

TCP80,83,7003

웹 컨텐츠 및 문자 보내기

Daum

211.233.29.78/32

TCP 8062

 

SayClub

211.233.47.20/32

 

 

AOL

 

TCP 5190

AOL Instant Messenger Also used by: ICQ

UDP 4000

ICQ_locator

Dreamwize

211.39.128.236/32

211.39.128.184/32

TCP 10000

 

버디버디

 

TCP 810

 

TCP 940

 

TCP 950

 

케이친구

 

TCP 7979

 

천리안

 

TCP 1420

 

TCP4949, 8989

파일 송수신

ICQ

 

TCP 5190

 

UIN

 

TCP 8080

 

Genile

 

TCP 10000

 

 

▶ P2P 관련

service name

TCP

UDP

소리바다

22322, 22323, 7675

22321, 7674

당나귀

4661, 4662, 4665

8719, 4665, 4672

구루구루

9292, 9293, 8282, 31200

 

Direct

411-412

411-412

Gnutella

6346, 6347

 

GoBoogy

 

5325

Hotline

5497, 5498, 5500, 5501, 5503

 

KaZaA

1214

 

Madster

23172, 9922

 

Maniac

2000, 2222

2010

V-Share

8401-8404

8401-8404

shareshare

6399, 6777

 

WINMX

6699

6257

엔유

8185, 8184

 

파일구리

9493

9493

파일피아

8090-8091

 

iMash

5000

 

BitTorrent

6881, 6889

 

Guntella-Morpheus

6346-6347

6346-6347

GuRuGuRu

9292, 8282, 31200

 

Madster-Aimster

23172, 9922

 

MiRC

6667, 6665-6670, 7000

 

Bluster

 

41170

GoToMyPc

8200

 

Napster

6600-6699, 4444, 5555, 6666, 7777, 8888, 8875

 


▶ Game 관련

service name

TCP

UDP

스타크래프트

6112, 1156-1158

6112, 1156-1158

[펌]디스크없이 원격으로 부팅하기

<퍼온 사이트 : http://www.cep.kr/blog/52?TSSESSION=4f98481c5f3797791b64d03f490b6566 >

Linux Diskless Cluster

5th Edition

기상연구소 예보연구실

*박광기

도종관

이용희

2002.5.28

1. H/W Setting

1.1 Master Node Bios Setting

1.2 Slave Node Bios Setting

1.3 Slave Node Onboard Lancard 부팅 셋팅

2. Clustering

2.1 Clustering에 필요한 package

2.2 Master node Setting

2.2.1 HDD partitioning

2.2.2 TFTP setting

2.2.3 DHCP setting

2.2.4 NIS setting

2.2.5 RSH setting

2.2.6 syslog

2.2.7 /etc setting

2.2.8 kernel setting

2.3 Slave node Setting

2.3.1 sdct

2.3.2 /tftpboot/Template

2.3.3 slave node kernel

2.3.4 adcn

2.3.5 test node debug

2.3.6 slave node 추가

3. 필요한 소프트웨어 설치

3.1 MPICH

3.2 PG COMPILER

3.3 OpenMP

3.4 PVM

3.5 NETCDF

3.6 NCARG

4. 추가적인 기술들

4.1 PXE

4.2 Channel Bonding

4.3 MON

4.4 MOSIX

4.5 M-VIA

4.6 MYRINET

4.7 BPROC

5. 참고 자료

6. Benchmark

6.1 H/W 성능에 따른 성능 비교

6.2 Network 속도에 따른 성능 비교

1. H/W Setting

1.1 Master Node Bios Setting

* Bios setting

[Advanced]

Keyboard Configuration

--> Keyboard Error : Ignore Error

[Boot]

Removable Devices

HDD <-- Master이므로 HDD로 부팅하는 것이 정상.

MBA UNDI (Bus0 Slot15)

MBA UNDI (Bus0 Slot16)

1.2 Slave Node Bios Setting

* Bios setting

[Advanced]

Keyboard Configuration

--> Keyboard Error : Ignore Error

[Power]

ACPI Enabled : No

Power Savings : Disabled

[Boot]

MBA UNDI (Bus0 Slot15) <-- OnBoard Network Boot사용하기 위해.

1.3 Slave Node Onboard Lancard 부팅 셋팅

* Bios 셋팅이 끝나면 ( Boot가 MBA UNDI인 경우 ) 시스템이 재가동된다. 이때 화면상에 "Control + Alt + B" 가 나오면 그대로 따라 누른다. 이렇게 하면 onboard lancard 붙팅에 관련된 옵션을 셋팅 할 수 있는 창이 뜬다. 이때 다음을 수정한다.

Boot Method : TCP/IP

Protocol : DHCP (우리가 주로 DHCP를 사용할 것이므로 )

Config message : Enabled

Message Timeout : 3 seconds

Boot Failure Prompt : Wait for time out

Boot Failure : Reboot ( 실패하게되면 시스템을 자동으로 재가동하게 하기위해)

F10을 눌러서 저장하고 나온다.

2. Clustering

2.1 Clustering에 필요한 package

DHCP

dhcp-common

dhcp-server

dhcp-client

TFTP

tftp

tftp-server

tftpboot-script

adcn

sdct

Etherboot

mknbi ( etherboot )

imggen ( Onboard Lancard rom boot )

RSH

rsh-server

rsh

NIS

ypbind

yp-tools

ypserv

mawk (이 패키지가 없으면 make를 하여도 ypcat하면 자료가 나오지 않는다. make할 때 조금 문제가 발생한다. mawk대체 품은 awk이다.

(Makefile에서 수정해 주면 됨))

CHANNEL BONDING

2.4.x부터는 kernel에 포함되어 있으므로 kernel컴파일시 channel bonding을 모듈로 켜놓으면 됨.

2.2 Master node Setting

2.2.1 HDD partitioning

Diskless로 클러스터를 구성하면 hard link를 해야 경우가 있다. 그런데 hard-link는 동일한 파티션내에서는 문제가 되지 않지만 다른 파티션 사이에서는 하드링크를 걸 수가 없기 때문에 node수만큼 필요한 공간을 사용하기 위해 root(/)를 크게 하고 그곳에 node용 NFS-root 영역을 잡는 것이다. 따라서 추가되는 노드수에 대비하여 충분히 크게 잡아주는 것이 좋다.

/ : node수에 맞게 적당히 ( node당 ( 150MB ~ ) 200MB정도 계산함 )

/usr : 15G정도 ( 많은 소프트웨어 깔므로... )

/home : User Data용

2.2.2 TFTP setting

/etc/xinetd.d/tftp 파일을 vi로 열어서 'disable = yes'로 된 부분을 'disable = no' 로 바꿔주면된다. 만약에 수동으로 하고 싶지 않다면 redhat 계열 linux같은 경우에는 setup 명령을, mandrake linux같은 경우에는 ntsysv 명령을 통하여 service중 tftp를 체크해 주면 된다. 이 tftp 셋팅은 rpm 패키지를 설치한 후에 서비스를 가동시켜주기만 하면 된다. 이유는 dhcp 또는 bootp서버를 이용할 때 TFTP는 kernel전송용 FTP 역할만 하기 때문에 별도로 고려해야 할 것이 없다. TFTP를 가동할 때는 /etc/rc.d/init.d/xinetd restart를 해주면 된다. (오래된 배포판은 /etc/rc.d/init.d/inet restart )

( 사용자인증과정 없이 서버에 지정된 위치의 파일을 요구하면, 파일을 전송해 주

는 방식. 보안은 취약하지만 아주 기본적인 파일 전송만 하면 될 때 많이 쓰임.

주로 네트워크 장비들의 펌웨어 등을 업그레이드할 때 많이 사용되어짐.)

TFTP 서버는 기본적으로 /tftpboot 디렉토리를 기본 디렉토리로 사용한다. 만약에 이 디렉토리를 사용하고 싶지 않다면 kernel source에서 /tftpboot로 된 것을 찾아 수정한 후에 kernel을 컴파일 할 경우에는 자신이 바꾼 디렉토리가 tftp서버의 기본 디렉토리가 된다.

sdct나 adcn을 돌리기전에 master node설정을 끝내고 sdct를 돌린다. 그리고 나서 /tftpboot/Template 설정이 끝나면 adcn을 돌리면 된다.

sdct를 돌리기 전에 /tftpboot가 있다면 문제가 되므로 /tftpboot 디렉토리를 지운 후에 sdct를 돌리면 된다.

2.2.3 DHCP setting

Diskless를 사용하는 것은 관리상 편리함이 가장 우선이고 또한 Hard disk의 가격부담을 줄이는 장점도 있다. 이 문서에서는 계산 전용 Cluster를 위주로 하여 설명하기 때문에 CPU자원을 최우선으로 고려하며 또한 문제 발생시 쉽게 CPU용 컴퓨터만 간편하게 재부팅해도 하여도 kernel이나 file system이 손상되는 등의 문제가 발생하지 않으므로 중요한 fileserver만 관리하면 되기 때문에 관리 및 유지상 장점이 있다. 이 문서에서는 diskless Clsuter를 만들기 위해서 사용하는 방법이 DHCP 또는 BOOTP 기능을 활용 수 있도록 다루었다. 그러나 BOOTP는 고전적인 방법이고 DHCP를 사용하는 것이 편리하여 BOOTP에 대해서는 환경 설정 파일만 간략히 설명하고 여기서는 DHCP를 중심으로 설명을 하였다.

[DHCP]

/etc에 있는 dhcpd.conf파일을 수정하고 나서 /etc/rc3.d/S*dhcp를 설정하여 부팅시에 자동으로 DHCP daemon이 가동되도록 설정하면 된다. 물론 이것 역시 /etc/dhcpd.conf 파일을 수정한 후에 간단하게 redhat 계열 linux에서는 setup명령으로 설정하고, mandrake linux에서는 ntsysv명령으로 dhcp service를 설정할 수가 있다.

실제 Node간에는 사설 IP로 설정하여 연산에만 사용할 수 있도록 하고 master node에만 접속 가능한 IP를 설정하는 예제이다. (사설 IP: 10.1.10.x/접속용 IP: 210.200.100.x라고 하자)

------------------------/etc/dhcpd.conf---------------------------------------

not authoritative;

#ddns-update-style none;

#log (info, concat ( "VCI: " , option vendor-class-identifier ) );

shared-network expo {

subnet 10.1.10.0 netmask 255.255.255.0 {

}

subnet 210.200.100.111 netmask 255.255.255.255 {

}

}

group {

default-lease-time 21600;

max-lease-time 21600;

use-host-decl-names on;

option domain-name "kma.go.kr";

option subnet-mask 255.255.255.0;

option broadcast-address 10.1.10.255;

option routers 10.1.10.10;

option domain-name-servers xxx.xx.xx.x;

option nis-domain "expo";

option nis-servers 10.1.10.10;

option log-servers 10.1.10.10;

option tftp-server-name "expo";

server-name "expo";

host expo1 {

hardware ethernet 00:E0:81:03:6E:27;

fixed-address 10.1.10.11;

option root-path "/tftpboot/10.1.10.11";

next-server 10.1.10.10;

filename "/10.1.10.11/boot/bzImage.mba";

}

host expo2 {

hardware ethernet 00:E0:81:03:6D:09;

fixed-address 10.1.10.12;

option root-path "/tftpboot/10.1.10.12";

next-server 10.1.10.10;

filename "/10.1.10.12/boot/bzImage.mba";

}

..........................................

host expo8 {

hardware ethernet 00:E0:81:03:6C:65;

fixed-address 10.1.10.18;

option root-path "/tftpboot/10.1.10.18";

next-server 10.1.10.10;

filename "/10.1.10.18/boot/bzImage.mba";

}

}

---------------------------------------------------------------------

option domain-name은 이 master node가 속해있는 domain name을 적어주면 된다. option subnet-mask에는 Cluster간 통신할 네트워크 Class를 C Class로 설정하였다. option broadcast-address는 Cluster간 통신할 네트워크 broadcast영역을 설정해준다. option routers는 Cluster의 master node의 Cluster IP를 써주면 된다. Cluster의 모든 통신은 Master node로부터 이루어지기 때문이다. option domain-name-servers는 이 cluster의 hostname을 등록해 dns서비스를 하는 서버의 IP를 적어주면 된다. option nis-domain은 master node에서 NIS를 통해 각 연산노드들의 계정을 일괄 관리하기 위해 NIS 기능을 사용하는데 이때 NIS서버가 역시 Master Node가 되므로 master node hostname을 써주면 된다. 물론 별도로 관리한다면 용도에 맞게 적어주면 된다. option nis-servers는 NIS서버의 hostname대신 IP를 써주면 된다.

option log-servers는 모든 node의 log내용을 master node에 쌓아두기 위해 설정하는 것이다. 그 이유는 각 node는 disk가 없기 때문이고 또한 관리를 편리하게 하기 위해서이기도 하다.

option tftp-server-name은 tftp로 각 노드의 kernel을 제공할 서버를 써주면 된다. 물론, master node가 각 node의 모든 kernel을 가지고 있고 또한 제공하기 때문에 master node의 hostname을 썼다. server-name는 master node의 hostname을 써주면 된다.

그리고 group안에 있는 host는 각 node의 hostname을 써주면 된다. 즉, 이 DHCP서버가 각 node의 lancard의 macaddress를 참조하여 hostname과 IP를 자동으로 그 host에 제공하게 된다. hardware ethernet은 slave node의 lancard macaddress를 적어주면 된다. 이 값은 etherboot용 floppy를 만들어 부팅 시켜 보거나 lancard driver flopy에 있는 utility를 활용하거나 windows에서 네트웍이 연결된 상태에서 command창을 띄워 netstat -nr 명령을 통하여 알아낼 수 있다. 기타 arpwatch등의 많은 방법을 통하여 알아낼 수 있다. fixed-address는 이 host가 받을 IP이다. 즉, expo1이라는 hostname을 받은 slave node는 10.1.10.11의 IP를 자동으로 부여받게 된다. option root-path은 tftpboot로부터 받아오는 root위치를 알려준다. next-server는 DHCP 서버를 제공하는 master node의 IP를 적어주면 된다. filename은 tftp로 갖고 갈 slave node의 kernel image의 위치를 적어주면 된다. 이런 방식으로 필요한 만큼의 node를 셋팅 하여 주면 된다.

이런 셋팅이 끝나면 /etc/rc.d/init.d/dhcpd restart명령을 통하여 dhcp 데몬을 재가동해주면 된다.

[BOOTP]

이는 /etc/bootptab또는 /etc/bootparams파일이 설정파일이다. 예를 아래에 들었다.

-------------------/etc/bootptab----------------------

global:sm=255.255.255.0:ds=172.16.24.1:gw=172.16.24.1:ht=ethernet:bf=bzImage.new:dn=cep.re.kr:sa=172.16.24.1:

test1:td=/tftpboot:hd=/172.16.24.2/boot:tc=global:ha=0050FC4F0EE3:ip=172.16.24.2:rp=/tftpboot/172.16.24.2:

test2:td=/tftpboot:hd=/172.16.24.3/boot:tc=global:ha=0050FC4F0AED:ip=172.16.24.3:rp=/tftpboot/172.16.24.3:

----------------------------------------------------

여기서 global아래에 적은 것은 모든 연산 노드들에서 공통적으로 사용될 내용임.

sm : sub netmask

ds : DNS서버 IP

gw : gateway IP

df : 부팅할 cluster client node kernel image file name

dn : cluster domain name

sa : tftp서버의 IP주소 ( 대게 cluster master node에 설정하므로 master node IP를 씀)

그리고 각 cluster client node들의 hostname을 시작으로 해서 각 node들의 환경을 셋팅 해 줌.

td : secure TFTP서버에서 사용되는 TFTP의 기본 디렉토리

hd : td와 같이 사용되어 커널 파일의 전체디렉토리 경로임. (TFTP는 td:hd디렉토리를 boot kernel 디렉토리로 인식하므로 /tftpboot/172.16.24.2/boot의 위치가 boot kernel이 있는 위치로 알게 됨.)

tc : global환경을 모두 포함하라는 의미임.

ha : cluster client node lancard hardware address (mac address)

ip : cluster client node IP

rp : cluster client node의 루트 파일시스템으로 마운트할 디렉토리 위치.

필요한 node들은 이렇게 test1, test2, test3, ...로 계속 적어주면 된다.

이렇게 설정이 끝났으면 bootp daemon을 가동시켜주면 된다.

2.2.4 NIS setting

NIS는 여러 host의 계정 관리할 때 매우 편리한 기능이다. 같은 계정을 여러 호스트에 발급하고 또한 관리하기 위해서 매번 여러 호스트에 접속하여 계정 관리를 하고 또한 패스워드도 각 호스트에서 모두 바꾸는 불편함 없이 한 호스트에서만 계정을 발급하고 또한 어떠한 호스트이던 간에 필요할 때 패스워드를 바꾸면 NIS로 묶여 있는 모든 host들의 계정 정보 및 password가 동시에 수정해 준다. 이 기능은 관리자에게 매우 매력적이고 편리한 기능이다. 물론 cluster에서도 각 node에서 연산역할만 하지만 프로그램이 수행되기 위해서는 각 node에 동일한 계정이 있어야 하며, 또한 동일한 계정 정보를 가지고 있어야 하므로 이곳에서 또한 NIS기능을 활용한다. 우선 /etc/yp.conf파일에 NIS정보를 추가해준다.

-----------------/etc/yp.conf-------------

domain expo server 10.1.10.10 <----------- 추가내용

----------------------------------------------------

domain expo server expo로 사용하여도 된다. 그러나 server뒤에 hostname을 쓰기 위해서는 DNS서버에 이 host에 대해 등록되어 있어야만 가능하다. 그렇기 때문에 DNS에 등록되진 않은 경우에는 직접 IP를 적어주어도 된다. IP를 적을 때에는 Cluster 내부에서만 인식할 것이므로 Cluster용 사설 IP를 사용하면 된다.

ypbind 데몬을 돌리기 위해서는 /etc/sysconfig/network 파일에 NIS서버를 설정해줘야만 NIS서버를 정상적으로 작동하게 된다.

---------------/etc/sysconfig/network--------------

NETWORKING=yes

FORWARD_IPV4=yes

HOSTNAME=expo

DOMAINNAME=kma.go.kr

NISDOMAIN=expo <---- NIS를 사용하기위해 추가

GATEWAY=190.1.40.1

GATEWAYDEV=eth3

--------------------------------------------

정보를 찾을 때 /etc/hosts파일에서 찾고 또한 DNS서버에서 찾게 설정되어 있다. 그러나 NIS를 사용하게 되면 네트워크 상에서 NIS서버에서 정보를 찾아야 하므로 다음처럼 /etc/host.conf 파일에 nis를 추가해준다.

-----------/etc/host.conf---------------

order hosts,bind,nis <---- nis 추가

multi on

------------------------------------

여기까지 되었다면 yp의 master를 현재 host에 설정하기 위해 다음 명령을 실행해준다.

/usr/lib/yp/ypinit -m

이것을 실행하게 되면 필요한 정보 등을 갖고 와서 yp용 DB파일을 /var/yp 디렉토리 하에 만들게 된다. 그리고 yp는 보안상 약간의 문제가 될 수 있으므로 다음처럼 yp기능을 제공한 네트웍 범위를 지정해줘야 한다.

--------------/var/yp/securenets----------------

255.0.0.0 127.0.0.0

255.255.255.0 10.1.10.0

255.255.255.255 xxx.xxx.xxx.xx

---------------------------------------------

yp관련 server가 잘 설정되었는지 확인하기 위해 /var/yp/ypservers 파일을 열어본다. yp server의 hostname으로 잘 되어 있다면 ypwhich 명령으로 ypserver를 한번 더 검색해본다. 물론 같이 잘 나온다면 yp설정이 정상적으로 된 것으로 생각하면 된다.

--------/var/yp/ypservers-----

expo

----------------------------

{NIS계정 추가하기}

NIS용 계정을 추가하기 위해서 /etc/passwd 아래에 +:*:0:0:::를 추가하고 그 아래에 계정을 일반적으로 수동으로 추가하듯이 추가하여 주면 된다.

이렇게 계정을 추가한 후에 (+:*:0:0::: 아래에다가) /var/yp 아래에서 make명령을 실행시켜주면 자동으로 계정이 yp db에 등록되게 된다. 한가지 TIP은 yp계정 한계선(+:*:0:0:::)위에 발급된 계정은 그 host에만 적용되며 (NIS로 적용 안됨.) 또한 그 host내에서는 NIS 계정보다 일반계정이 우선권이 있다. 그래서 NIS 계정과 일반계정이 같은 것이 있는 경우에는 그 host에서만은 일반계정이 우선적으로 설정한 내용이 적용된다. 물론 다른 NIS로 묶인 host에서는 NIS정보에 의해 적용된다.

yp그룹은 /etc/group 파일에 +:*:0:를 추가하고 아래에 마찬가지로 추가해주면 된다.

계정 발급 후에는 항상 /var/yp에서 make 명령을 한번씩 돌려주는 것을 잊어서는 안 된다. 이렇게 모든 것이 되었다 싶으면 yp 데몬을 한번 재 가동시켜준 후에 ypcat passwd 명령을 하였을 때 NIS용 계정 발급된 내용이 잘 보이면 NIS는 성공한 것으로 생각하면 된다. 그런데 한가지 yp 데몬들에 대해서는 잘 설정되었으나 ypcat으로 정보가 보이지 않는 경우에는 mawk 패키지가 깔려 있지 않은 경우이다. 그러니 mawk 패키지를 깔아주고 나서 /var/yp에서 make명령를 돌려주면 다시 db가 갱신되어 잘된다. mawk가 없는 경우에는 awk로 대체할 수가 있으므로 /var/yp/Makefile를 열어서 mawk로 된 것을 awk로 수정하여 실행하면 된다.

개인이 패스워드 바꿀 때는 yppasswd 명령을 이용하면 됨.

NIS Client에서 NIS 정보를 만들기 (yp관련 server 설정하기)

/usr/lib/yp/ypinit -s expo

여기서 expo는 NIS 서버 hostname이다.

2.2.5 RSH setting

rsh는 remote shell의 약자로 telnet처럼 login ID와 password를 이용하여 접속하여 사용하는 것이 아니라 ID와 패스워드 없이 다른 host로 쉬게 이동하거나 명령을 직접 실행가능 하게 해준다. 많은 컴퓨터를 관리할 때는 매우 편리하나 조금 보안상 문제가 발생할 수 있다. rsh를 일반사용자들이 사용하기 위해 설정해보자. /etc/hosts.equiv파일에 다음처럼 각 node의 모든 host이름을 적어주자. 만약에 한 host가 2개 이상의 네트워크를 사용하기 위해 2개 이상의 hostname을 갖고 있다면 모든 hostname으로의 network에서 rsh가 가능하게 하기 위해 모두 추가해줘야 한다.

------------/etc/hosts.equiv---------------

localhost

expo

expo1

...

expo8

exp <-- 이 아래는 channel bonding용으로도 rsh를 사용할 수 있도록 하기 위해

exp1

...

exp8

---------------------------

rsh의 보안 및 사용할 기능과 root사용자를 위해 다음처럼 /etc/securetty파일에 내용을 추가해준다.

-------------/etc/securetty----------------

rexec

rlogin

rsh

root@localhost

root@expo

root@expo1

....

root@expo8

-------------------------------------------

또한 root사용자가 rsh를 사용할 때 remote 명령이 안 되는 것을 풀기 위해 다음처럼 내용을 주석 처리해 준다.

------------/etc/pam.d/rsh-----------------

auth required /lib/security/pam_nologin.so

auth required /lib/security/pam_rhosts_auth.so hosts_equiv_rootok

----------------------------------------------

rexec기능을 root도 가능하게 하기 위해 다음처럼 파일에 내용을 추가해준다.

-------------------/etc/pam.d/rexec---------------

.....

auth sufficient /lib/security/pam_rhosts_auth.so hosts_equiv_rootok

...

-------------------------------------------------

rlogin를 root가 사용가능 하게 하기 위해 다음처럼 내용을 추가해준다.

-----------------/etc/pam.d/rlogin-----------------

...

auth sufficient /lib/security/pam_rhosts_auth.so hosts_equiv_rootok

...

---------------------------------------

마지막으로 /root디렉토리에 .rhosts파일을 만들어 준다.

--------------------/root/.rhosts---------------

localhost

expo

expo1

...

expo8

exp

exp1

...

exp8

---------------------------------

2.2.6 syslog

syslog는 각 node마다 각기 쌓이게 되면 관리하기가 불편하다 또한 각 node들이 disk가 없으므로 master node에 모든 log를 쌓는 것이 편리하다. 그렇게 하기 위해 다음처럼 설정하자.

-----------/etc/sysconfig/syslog--------------

SYSLOGD_OPTIONS="-r -m 0" <--- -r 옵션 추가

---------------------------------------------------

-r 옵션은 remote host에서 log정보를 보낼 경우, 받아서 자신의 log파일에 저장하겠다는 것이다. 이렇게 master node에 이것을 설정하고 slave node에 /etc/syslog.conf 에 내용을 master node로 보내게 하면 master node의 log파일에 모든 slave노드의 log까지 들어가므로 하나의 파일만 살펴보면 모든 node의 log를 볼 수 있게 된다.

slave node에서는 다음처럼 파일을 수정해주면 설정된 node로 모든 log내용이 전송된다. 그러므로 slave설정할 때 설정해주면 된다. 물론 adcn를 돌리게 되면 자동으로 master node로 log가 쌓이도록 자동 설정된다.

----------------- /tftpboot/Template/etc/syslog.conf------------------

*.* @expo

-----------------------------------------------------------------

2.2.7 /etc setting

각 node에 대한 cluster용 ip를 설정해준다. 대부분 정보가 hostname으로 사용되기 때문에 hosts파일에 다음처럼 설정해주면 된다.

-------------/etc/hosts--------------------

127.0.0.1 localhost.localdomain localhost

210.200.100.111 expo expo.kma.go.kr

# Cluster

10.1.10.10 expo

10.1.10.11 expo1

........ ...

10.1.10.18 expo8

# Bonding

10.1.100.10 exp exp0

10.1.100.11 exp1

..... ...

10.1.100.18 exp8

--------------------------------------------

/etc/ethers파일은 정확히(??) 뭐에 필요한건지는 모르겠지만 각 node의 lancard macaddress와 각 host의 hostname을 mapping시켜주는 파일이므로 아래 형식처럼 써주면 된다.

/etc/exports파일은 일부러 수정해줄 필요는 없다. 이유는 adcn을 돌리게되면 자동으로 /etc/exports파일이 수정되기 때문이다. 그러나 이 파일은 혹시라도 잘못 된 경우를 대비해서 참조하기 위해 적어둔다.

------------------/etc/exports----------------------

/tftpboot/10.1.10.11 expo1(rw,no_all_squash,no_root_squash,no_subtree_check)

/usr expo1(ro,no_all_squash,no_root_squash,no_subtree_check)

.... ...

--------------------------------------------------------

마지막으로 kernel 및 ip performance 등에 관련된 환경설정 파일이다.

-------------------------/etc/sysctl.conf---------------

net.ipv4.ip_forward = 1

net.core.rmem_default = 262144

net.core.rmem_max = 262144

net.core.wmem_default = 262144

net.core.wmem_max = 262144

#

sys.fs.file-max = 16384

-------------------------------------------------------

2.2.8 kernel setting

kernel을 컴파일 하기 전에 Makefile을 열어서 다음을 수정해준다.

----------/usr/src/linux/Makefile----------------

EXTRAVERSION =-2

--------------------------------------------

이 내용은 kernel compile후에 module를 컴파일 한 후에 module install시 module이 kernel-version-num뒤에 extraversion 이 들어가므로 module들이 서로 꼬이지 않아 좋다. 또한 kernel 적용 버전이 될 수도 있어 편리하다. 이 부분을 잘 활용하면 된다.

master용 kernel setting에 있어 나머지는 각 시스템에 맞게 각자 관리자의 목적에 맞게 설정하면 된다. 그러나 아래의 셋팅은 master node에 있어 꼭 필요한 정보이므로 아래 내용만큼은 꼭 넣어야 하며 나머지는 각 시스템에 맞게 설정해주면 된다.

------------------------------------------------------------------

Code maturity level options

--> Prompt for development and/or incomplete code/drivers --- y

File System

--> Kernel automounter support ---- y

--> Kernel automounter version 4 support (also support v3) ---- y

--> Network File System

--> NFS file system ---- y

--> Provide NFSv3 client support ---- y

--> NFS server support ---- y

--> Provide NFSv3 server support ---- y

--------------------------------------------------------------------

( USB Mouse가 필요한 경우에는 다음 부분을 살펴보면된다.

Input core support

--> mouse support --- y

USB support

--> USB Human Interface Devices (HID)

--> USB HIDBP Mouse (basic) support --- y )

설정이 끝났으면 Save configuration to An Alternate File을 이용하여 master node kernel setting값을 저장해둔다. 그래야 나중에 조금 설정 값을 바꿀 때 이 값을 불러들여서 수정하면 편리하기 때문이다. 이제 설정이 끝났으므로 컴파일을 해야 한다. 컴파일을 하기 위해서는 다음처럼 명령을 이용하면 된다.

make dep; make clean; make bzImage; make modules; make modules_install

위의 명령어를 이용하여 master node kernel을 compile하고 또한 module들을 컴파일하고 module들을 인스톨한다.

+ linux kernel setting 및 lilo 설정.

cp /usr/src/linux/arch/i386/boot/bzImage /boot

cp /usr/src/linux/System.map /boot/System.map-kernel.version.num-EXTRAVERSION

vi /etc/lilo.conf을 해서 아래처럼 master node kernel 부팅을 추가해준다.

-------------------------

image=/boot/bzImage

label=master

root=/dev/hda5 ( / partition )

read-only

-------------------------

lilo 명령을 이용하여 kernel setting한 것을 boot roader에 적용한다.

2.3 Slave node Setting

2.3.1 sdct

sdct는 /tftpboot/Template라는 디렉토리에 master node의 root partition에 있는 내용들을 hard link로 만들어주는 역할을 한다. /tftpboot/Template가 모든 컴퓨팅 노드의 구성을 변경하는 곳이 된다. 이곳의 파일이 바뀌면 앞으로 만들게 될 다른 cluster client node들의 파일도 전부 바뀌게 된다. /tftpboot/Template ( Master node root ), /tftpboot/IPs ( Cluster client node root )가되는 것이다. 그렇기 때문에 /tftpboot/Template 디렉토리 아래에 있는 파일을 자신의 클러스터에 맞게 변경하면 된다.

sdct -s 10.1.10.10

그러나 이것은 초기버전 에서는 조금 수정해야 할 부분이 많다. 그래서 필자가 수정한 파일을 이용하여 주면 조금은 더 편리하다.

sdct -N 10.1.10.10 -P /usr/local/src/cluster/bin [ -s 10.1.10.10 ]

여기서 -N은 NFS-ROOT서버의 IP주소를 써주면 되고 또한 -P 뒤에는 기타 환경 설정용 파일이 존재하는 위치를 적어줌. -s 뒤에는 master node의 IP를 써주면 됨. -s 부분은 굳이 적지 않아도 자동으로 찾아 적힌다. 필자가 수정한 sdct파일은 다음 파일도 필요 한다. sdct.ext.fstab 이 파일은 fstab을 만들어주는데 있어 부수적으로 더 필요한 설정이 들어가 있는 파일이다.

2.3.2 /tftpboot/Template

이 위치에서는 etc/rc0.d와 etc/rc3.d 그리고 etc/rc6.d 디렉토리의 실행순서 등을 만들고 불필요한 파일은 삭제를 해줘야한다. 만약에 XWindows를 사용한다면 etc/rc5.d를 사용하게 될 것이다. 이 부분은 slave하나를 셋팅해 놓고 켜보면서 뜨는 message를 보면서 파일순서 또는 필요 없는 데몬을 삭제하면 된다.

/etc/rc.d/init.d/netfs의 시작순서가 /etc/rc.d/init.d/network 다음에 오도록 수정한다.

/etc/rc.d/rc3.d/S10network와 /etc/rc.d/rc3.d/S11netfs로 만들어준다.

/etc/rc.d/rc2.d/K10netfs와 K20network는 가장 큰 숫자로 바꿔준다.

한가지 /etc/init.d/killall과 halt란 파일을 수정하지 않으면 재부팅 및 shutdown시 killall process이후에 멈추는 문제가 생긴다.

                  2.3.3 slave node kernel

물론 이것 역시 /usr/src/linux/Makefile을 vi로 열어서

----------/usr/src/linux/Makefile----------------

EXTRAVERSION =-2

--------------------------------------------

이 부분을 수정해준 후에 다음 작업을 해주는 것이 좋다.

master용 kernel configure를 불러들인다. 그리고 나서 다음 항목을 수정한다.

------------------------------------------------------

Networking Options

--> IP: kernel level autoconfiguration

--> IP: BOOTP support ---- y

File system

--> Network File System

--> Root file system on NFS ------ y

----------------------------------------------------

그러나 slave용 node에서는 NFS-ROOT용으로 사용할 Network card는 kernel속에 직접 넣어야 한다. 그래야만 kernel을 tftp로 갖고 가서 kernel을 load하면서 network이 구성될 수가 있기 때문이다. 이렇게 하지 않으면 network용 module을 올리기전에 NFS-ROOT가 올라오지 않아서 문제가 되기 때문이다. 그리고 나서 다음처럼 커널을 컴파일 해준다.

참고로 vmware를 사용할 경우 vmware용 렌카드는 lancepci.lzrom(또는 amdhomepna.lzrom)을 사용하면 된다.

Network device support

--> Ethernet (10 or 100Mbit)

--> AMD LANCE and PCnet (AT1500 and NE2100) support ---y

accton lancard는 tulip driver를 사용한다. tulip driver는

Network device support

--> Ethernet (10 or 100Mbit)

--> EISA, VLB, PCI and on board controllers

--> DECchip Tulip (dc2114x) PCI support ---y

make dep; make clean; make bzImage

명령을 이용하여 커널을 컴파일 한다.

이제 컴파일이 끝난 후에 bzImage와 System.map파일을 /tftpboot/Template/boot에 카피해 놓자. 그리고 나서 etherboot용 kernel로 만들기 위해서 만들어진 kernel을 조금 수정해야 한다. 먼저 etherboot용 프로그램을 컴파일 해놓고 다음처럼 프로그램으로 수정해주면 된다.

mknbi 프로그램을 이용하여 network boot가 가능하게 kernel image시작부분에 코드를 추가해준다. 그리고 바뀐 kernel image와 System.map파일이 /tftpboot/Template/boot에 존재해 있게 해주면 된다.

mknbi와 etherboot파일은 http://etherboot.sourceforge.net/ 또는 http://etherboot.sourceforge.net/distribution.html 이곳에서 다운받을 수 있다.

mknbi --format=elf --target=linux --output=bzImage.new --ip=dhcp bzImage

--format : nbi로 하면 커널 로딩을 못한다. elf로 하여야 한다.

--target : linux에서 사용할것이므로 linux로 한다.

--output : kernel image를 변경후 저장할 파일명이다.

--ip : dhcp데몬을 사용할 것이면 dhcp bootp를 사용할 것이면 bootp를 씀.

만약에 etherboot가 아닌 onboard용 lancard를 이용한 rom 부팅을 한다면 다음 프로그램을 이용하여야 하며 이용법은 다음과 같다.

/usr/local/sbin/imggen -a bzImage.nbi bzImage.mba

그러나 mknbi를 먼저 실행한 후에 mknbi를 수행한 kernel image파일을 같고 위처럼 바꾸기 때문에 mknbi역시 꼭 필요한 것이다.

diskless cluster node로 부팅하기 위한 부팅 디스크 만들기를 만들어 보자. 이것은 부팅 image와 간략한 lancard정보만 들어 있는 아주 작은 image파일이다.

ehterboot는 gcc 2.96에서는 컴파일이 되지 않는다. 2.95이하 또는 3.0이상에서만 컴파일이 된다. 한가지 문제점은 컴파일이 잘되고 부팅 이미지 만들어 부팅하면 부팅이 잘되어 찾기는 하지만 찾는 중으로 멈춰 있으면 다음 3가지를 점검하자.

a) cable 연결상태 확인.

b) dhcp 또는 bootp 설정상태.

c) nds상태

e) 컴파일은 되었지만 잘못 컴파일 된 경우 ( 이때는 kgcc 로 컴파일해보자 ).

etherboot는 src디렉토리의 Config 파일이 bootp로 할지 dhcp로 할지 결정하는 옵션 등이 있다. 적당히 수정한다.

make; make bin/boot1a.bin

명령으로 컴파일을 한다.

boot1a.bin은 부팅 image인데 이것은 위처럼 make 명령을 따로 줘야 한다.

cat bin/boot1a.bin bin32/xxx.lzrom > /dev/fd0

명령으로 부팅 이미지를 floppy디스크에 넣는다. 여기서 xxx.lzrom은 cluster client node 컴퓨터에 붙어 있는 렌카드에 맞는 image파일이다.

2.3.4 adcn을 이용한 slave node 추가

adcn을 사용하여 cluster client node의 루트 파티션 설정하기.

이 파일은 각 client node들의 root partition을 잡을 때 사용하는 스크립트로, 필요한 정보는 각 노드의 IP주소, 각종 네트워크 정보, 그리고 그 노드에 있는 Lan card의 하드웨어 주소 등으로 되어 있다.( 하드웨어 주소는 16진수 6자리 숫자로 된 것으로 세계에서 그 렌카드의 하드웨어 주소는 오직 하나임. ) 자세한 설명을 “adcn -h“ 로 살펴보면됨. adcn을 사용하기 전에 아래의 cluster client node 설정하는 부분을 보고 client node용 kernel image를 만들어두고 adcn을 사용하면 편리하다.

adcn -i 10.1.10.12 -c cpu1 -d cep.re.kr -D eth0 -n 255.255.255.0 -s 10.1.10.10 -N 10.1.10.0 -g 10.1.10.10 -b 10.1.10.255 -m 00:D0:80:10:DC:70 -f

-i : 해당 컴퓨터의 cluster client node IP주소임.

-c : 해당 컴퓨터의 cluster client node의 host name임.

-d : 현재 cluster의 domain name이됨.

-D : cluster client node에사 사용될 Lancard의 interface이름을 써주면됨. (lancard가 하나이면 기본적으로 eth0 임.)

-n : netmask를 써줌.

-s : cluster Master node IP를 써주면 됨.

-N : Network를 써주면됨. ( cluster client node 네트워크 주소가 172.16.24.0가됨 )

-g : Network의 기본 네트웍 gateway 주소임.

-b : broadcast용 IP주소.

-m : cluster client node의 Lancard hardware address( Mac address )

adcn을 사용하면 /etc/hosts와 /etc/exports(NFS서버에 필요함) 그리고 /etc/fstab파일도 자동으로 각 node들에 맞게 수정됨.

이 과정은 루트파일시스템을 NFS로 접근하게 만드는 과정이다. (/usr/src/linux/Documentation/nfsroot.txt 참조)

이 내용 역시 손본 것이 많기 때문에 필자가 수정한 것을 사용하면 조금 더 편리하게 수정할 수 있다.

필자가 수정한 adct에는 -M 옵션과 -P 옵션이 있다. 여기서 -M옵션은 NIS 서버의 hostname을 써주면 되고, -P는 기타 부수적인 환경설정 파일이 존재하는 위치를 써주면 된다. 그리고 한번에 여러개의 slave를 만들기 위해서는 여러번 실행하려면 매우 불편할 것이다. 그래서 다음처럼 해보자.

---- slave라는 실행 파일을 하나 만들고 내용을 추가하자.----------------

#!/bin/bash

for AA in $(cat node) ; do

./adcn -i 10.1.10.1$AA -c cpu$AA -M cpu0 -P /usr/local/src/cluster/bin -d cep.re

.kr -D eth0 -n 255.255.255.0 -s 10.1.10.10 -N 10.1.10.0 -g 10.1.10.10 -b 10.1.10

.255 -m 00:50:FC:4F:0E:E3 -f

done

----------------------------------------------------------------

그리고 node라는 파일을 하나 만들어 node의 번호들을 죽 적어 둔 후 slave라는 파일을 실행시키면 각 노드의 slave를 adcn를 실행시켜줄 것이다.

물론 필자가 수정한 adcn이란 파일은 adcn.ext.exports란 파일이 필요하며 이 파일은 추가적으로 exportfs시키는 정보가 들어가는 파일이다.

2.3.5 test node debug

우선 한 개의 slave node를 만들어서 계속 부팅 해가며 문제점을 모두 잡은 후에 모두 문제가 해결되면 나머지 node에 대해서 만들어 주면 cluster완성되는 것이다.

만약에 cluster 이후 rsh에서 명령어가 안될 때 다음을 채크해보자.

/dev/console 파일의 퍼미션이 644로 안되어 있는 경우가 있다. 이런 경우 rsh로 login은 되어도 명령어가 실행되지 (rsh cpu1 ls) 않는 경우가 있다. 이럴 때는 chmod 644 /dev/console 해주면 된다.

가끔 keyboard가 이상할 경우 tset 과 reset 명령을 내려본다. 그러면 이상하던 keyboard가 정상으로 돌아온다.

3. 필요한 소프트웨어 설치

3.1 MPICH

mpirun -v -machinefile machine.cluster.node -nolocal -np 8 mm5.mpp.mpich

여기서 -nolocal 이던가 -nolocalhost이던가??를 해주는 것은 현재 돌리는 node에서는 계산을 하지말고 machine.cluster.node파일에 나열한 node hostname들에서만 계산작업을 하게 하는 것이다.

3.2 PG COMPILER

3.3 OpenMP

3.4 PVM

3.5 NETCDF

3.6 NCARG

4. 추가적인 기술들

4.1 PXE

4.2 Channel Bonding

channel bonding은 linux kernel에서 지원하고 H/W적 slot이 지원되는 한도 내에서 개수를 늘릴 수 있다.

channel bonding은 가상 network device를 만들고 (bond0) 그 device에 실제 물리적 device를 붙여서 같은 mac-address와 같은 IP를 같게 만들어준다. bonding시키는 모들 실제 물리적 device를.... 그러나 mac-address는 실제 device중 가장 먼저 bond0에 붙는 것의 mac-address를 가져가서 가상 device인 bond0의 mac-address가 되어 진다. 하나의 IP로의 네트웍이 실제 물리적으로 분리된 network 경로를 통해 네트웍이 분산되어 나가므로 그많큼 네트웍 bandwidth가 넓어지는 것이다. 하나보단 2개일 때 대략적으로 2배의 bandwidth가 넓어졌다.

1. Kernel에서 channel bonding을 y로 해준다.

2. ------/etc/modules.conf------------

alias bond0 bonding

options bond0 miimon=100

---------------------------------

3. /etc/sysconfig/network-scripts/ifcfg-bond0을 만들어주고 내용은 eth0와 비슷하게 IP를 하나 추가한다. 만약에 NFS-ROOT를 사용중이라면 (eth0에서) eth0와 비슷하게 Network Classs가 다르게 잡아준다.

예) 10.1.10.x를 NFS-ROOT로 사용중이라면

bond0 의 IP는 10.1.100.x를 사용해라. 안 그러면 네트워크이 혼동되어서 네투워크이 꼬일 경우도 있다.

4. /etc/rc.d/rc.local 내용추가

------------------------------

/sbin/ifenslave bond0 eth1

/sbin/ifenslave bond0 eth2

------------------------------

4.3 MON

4.4 MOSIX

M-VIA는 네트웍 속도에는 크게 영향을 주지 못하고 단지 네트웍을 심하게 사용하기 위해 CPU가 사용하는 load를 줄여준다. Network을 cpu가 관리하는 것을 lancard가 직접 관리하도록 해주는 프로그램이다. 실제 테스트를 하여보면 Network을 사용하기 위한 CPU load를 50%가까지 줄여준다. 그러므로 CPU는 계산에 더 사용하고, Network 사용에는 더 줄일 수 있다. 한가지 이것을 사용하려면 기존의 lancard용 device는 모듈로 만들어 주거나 아예 없애야 한다. 왜냐면 m-via용 device가 대신해서 module로 떠야하기 때문이다. 이렇기 때문에 NFS-ROOT용으로 사용하는 lancard는 m-via를 사용할 수가 없다. 왜냐면 NFS-ROOT용 lancard device는 kernel에 직접 넣어두어야만 network boot를 통해 NFS-ROOT를 사용할 수가 있기 때문이다.

참조: http://spcc.uos.ac.kr/clustering/mvia.html

- 지원되는 Lancard

DEC Tulp (kernel 2.2 and 2.4에서 지원) : 장시간 가동시 하드웨어 문제 같아 보이는 현상으로 죽어버린다.

Intel pro 10/100 (kernel 2.2 and 2.4에서 지원)

3com (kernel 2.4에서 지원)

Packet Engines GNIC-I gigabit (kernel 2.2.에서 지원)

Packet Engines GNIC-II gigabit (kernel 2.2.에서 지원)

Syskonnet SK-98XXX gigabit (kernel 2.2 and 2.4에서 지원)

- SMP 지원됨.

- Install

Makefile.config 파일에서 Kernel version/ SMP사용여부/ 깔릴 DEVICE Directory 등 수정

-----------------Makefile.config------------------------

# The version of the Linux kernel for which M-VIA is being built.

LINUX_VERSION = 2.4.9-4 <===== 리눅스 커널버전(현재 사용중인 커널의 커널 버전을 써준다. 이유는 나중에 module이 깔리때 들어갈 /lib/module하의 디렉토리를 맞추기위해서이다.

# Set MVIA_SMP to 1 for SMP machines, 0 for uniprocessors.

MVIA_SMP = 1 <== SMP사용여부

# The location of the Linux kernel source.

LINUX_SRC = /usr/src/linux-2.4.9 <== 현재 사용중인 커널의 소스위치

# The directories to install M-VIA user files in.

INCDIR = $(BASEDIR)/include

LIBDIR = $(BASEDIR)/lib

DEVDIR = $(ROOTDIR)/dev <== M-VIA용 디바이스가 설치될 위치 (Master일때와 slave일때의 위치가 다르므로 잘 생각 : 현재 master용)

#DEVDIR = /tftpboot/Template/dev (slave용 위치)

----------------------------------------------------

make후 인스톨하면 다음이 설치된다.

/lib/modules/kernel-version-num/net 하에 설치됨.

공통으로 사용되는 모듈: via_ka.o via_lo.o via_ering.o

각 카드의 모듈들 : via_3c59x.o (3c59x용) via_eepro100.o (intel용) via_tulip.o (DEC용)

/usr/local/lib/libvipl.a

/usr/local/include/vipl.h 가 또 설치되어 진다.

이렇게 설치후 모듈을 module map파일에 등록하기위해

depmod -a kernel-version-num 명령으로 등록시켜준다.

/dev 디렉토리에 필요한 device(via_lo via_eth1 via_eth0 ...)를 만들기위해 다음명령어를 사용한다.

make devices

/etc/modules.conf파일을 수정해준다.

----------modules.conf---------------

alias char-major-60 via_lo

alias eth2 via_eepro100

-----------------------------------

/etc/sysconfig/network-script/ifcfg-eth2를 생성해도 되지만 잘 되지 않는 편이어서 /etc/rc.d/rc.local파일에 ifconfig명령으로 뜨게 해준다.

/etc/vip_hosts파일 생성

------vip_hosts---------

00:03:47:B2:5B:18 exp

00:03:47:B2:22:BF exp1

00:03:47:B2:5B:19 exp2

00:03:47:B2:55:03 exp3

-----------------------

앞에는 각 호스트의 m-via로 사용할 lancard mac-address고 뒤는 host의 hostname이다.

추후에 m-via를 이용하여 channel bonding을 해보면 어떨지??? 아마도 UTP gigabit을 두개정도 사용하면 myrinet정도의 효과가 나오지 않을까?? 생각한다. 가격 대 성능 비에서 좋지 않을까???

한가지 문제점은 M-VIA를 이용하여 네트웍 구성 후 MPI를 사용하기 위해서는 MVICH를 깔아야만 하는데 네트웍 구성을 되었지만 MPI를 사용하기 위한 MVICH가 문제가 있어 잘 깔리지 않는다. 이것만 해결되면 한번 사용해 봄직 할 것 같다.

4.5 M-VIA

4.6 MYRINET

4.7 BPROC

5. 참고 자료

1. diskless node script

ftp://ftp.sci.usq.edu.au/pub/jacek/beowulf-utils/disk-less

2. Root file system over NFS

/usr/src/linux/Documentation/nfsroot.txt

3. Myrinet

http://www.myri.com

4. Etherboot

http://etherboot.sourceforge.net

5. OpenPBS

http://www.openpbs.org

6. MOSIX

http://www.mosix.org

7. MPICH

http://www.mcs.anl.gov/mpi/mpich/index.html

8. OpenMP

http://www.openmp.org

9. PXE

http://www.lanworks.com

10. GFS

http://www.sistina.com/products_gfs.htm

11. PVFS

http://www.parl.clemson.edu/pvfs/index.html

12. spcc.uso.ac.kr 은 각종 벤치마크가 잘되어 있다

prev 1 2 3 4 5 next