RSS구독하기:SUBSCRIBE TO RSS FEED
즐겨찾기추가:ADD FAVORITE
글쓰기:POST
관리자:ADMINISTRATOR

Ajax에서 서버와의 통신을 위한 핵심 요소인 XMLHttp는

IE7과 모질라 계열 브라우저에서는 native XMLHttpRequest를 사용.
그리고 IE6 이하 버전에서는 Microsoft.XMLHttp를 ActiveXObject로 object 생성.

[ IE에서 생성할 수 있는 XMLHttp object ]
  - Microsoft.XMLHttp
  - MSXML2.XMLHttp
  - MSXML2.XMLHttp.3.0
  - MSXML2.XMLHttp.4.0
  - MSXML2.XMLHttp.5.0


[ Cross-Browser 를 위한 XMLHttp object 메소드 생성 예 ]

function CreateXMLHttp()
{
  if (window.XMLHttpRequest)
  {
    return new XMLHttpRequest();
  }
  else if (window.ActiveXObject)
  {
    var aVersions = [ "MSXML2.XMLHttp.5.0"
      ,"MSXML2.XMLHttp.4.0"
      ,"MSXML2.XMLHttp.3.0"
      ,"MSXML2.XMLHttp"
      ,"Microsoft.XMLHttp"
      ];

    for (var i = 0; i < aVersions.length; i++)
    {
      try
      {
        var oXmlHttp = new ActiveXObject(aVersions[i]);
        return oXmlHttp;
      }
      catch (oError)
      {    
      }
    }
  }
}


[XMLHttp의 메소드 , 프로퍼티 및 이벤트]
1. 메소드
- abort( ) : 요청을 취소
- getAllResponseHeaders( ) : 모든 응답 헤더를 수신
- getResponseHeader(header) : 특정 응답 헤더 수신
- open(RequestType, url, async) : 통신 연결(nativ XMLHttpRequest는 Cross-domain을 제한함.)
   - RequestType : Get or Post

   - url : 서버 주소
   - async : true(비동기 방식 통신), false(동기 방식)
  
- send(content) : 서버로 요청을 보냄. content는 null 을 포함함.
- setHeader(header, value) : 헤더의 키와 값의 쌍을 설정

2. 프로퍼티 / 이벤트 핸들러
- onreadystatechange : readyState가 변경 시 실행될 함수 등록
- readyState : 요청 및 처리 상태
   - 0 (Uninitialized) : 아직 open() 메소드를 호출 하지 않은 상태
   - 1 (Loading) : open() 메소드를 호출한 상태(아직 send는 하지 않은 시점)
   - 2 (Loaded) : 요청을 서보로 보낸 상태( send() )
   - 3 (Interactive) : 일부만 응답 받은 상태
   - 4 (Complete): 모든 데이터를 받고 연결이 끊어진 상태

- responseText : 서버로 부터 받은 결과값의 스트링 반환
- responseXML : 서버로 부터 받은 결과값의 XML 형식
- status  : 서버로 부터 응답 받은 상태 코드 (200 (OK) or 404 (Not Found) 등)
- statusText : status 코드에 대한 의미 명기


[ 서버 전송 예 ]
function sendRequest()
{
    var content = getRequestBody();

    var oXmlHttp = createXMLHttp();
    oXmlHttp.open("post", "http://www.text.com/testForm.aspx", true);
    oXmlHttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

    //서버로 호출이 성공 하였을 경우 처리할 CallBack 함수 등록
    oXmlHttp.onreadystatechange = function ()
    {
        if (oXmlHttp.readyState == 4)
        {
            if (oXmlHttp.status == 200)
            {
                saveResult(oXmlHttp.responseText);
            }
            else
            {
                saveResult("An error occurred: "+ oXmlHttp.statusText);
            }
        }
    };
    
    oXmlHttp.send(content);
}


----------------------- 에이작스 한글깨짐 -----------------------------

JavaScript --> PHP

encodeURIComponent( string ) --> iconv( "UTF-8", "CP949", rawurldecode($string ) )


PHP --> JavaScript

rawurlencode( iconv( "CP949", "UTF-8", $string ) ) --> decodeURIComponent( string )


JSP/Servlet 에서 Ajax와 한글 인코딩 문제


Ajax는 기본적으로 UTF-8 만으로 통신을 한다고 보면된다. 그 이외의 Encoding을 지원하는 인코딩 함수가 없기 때문에 EUC-KR로 데이터를 전송하려는 꿈은 접어야만 한다.
헌데, 아직 우리나라는 EUC-KR 인코딩으로 된 웹 어플리케이션이 압도적으로 많은 상황이다(어서 빨리 UTF-8로 옮겨가길 바라마지 않는다).
거기다가 보통 JSP/Servlet 웹 어플리케이션은 Servlet 스펙 2.3이후부터 문자 인코딩 서블릿 필터를 사용해 모든 요청에 대해 일관된 문자 인코딩을 사용하는 것이 보편적인 방법으로 자리잡았다.

서블릿 필터는 일관성있게 모든 요청을 EUC-KR로 받아들이게 했는데, 몇몇 Ajax관련 요청만 UTF-8로 받아들여야만 하는 것이다.
필터를 적용할 URL-Pattern을 따로 줘보려 했으나, 너무 복잡해졌다.
그래서 HTTP 요청의 헤더를 이용해서 해결 했다.

아.. 한가지 더. 현재 한글 문제는 "XMLHttpRequest 요청 -> JSP/Servlet" 이 상황에서만 발생하는 것이다.
"JSP/Servlet -> XMLHttpRequest"의 상황(서버에서 클라이언트로 값을 리턴)에서는 이 문제가 발생하지 않는다.
서버가 리턴하는 문자열은 간단하게 다음처럼 하면 WAS가 자동으로 UTF-8로 값을 변경해서 전달하기 때문이다.

<%@ page contentType="text/plain; charset=utf-8" pageEncoding="EUC-KR"%>


contentType에서 text/plain은 텍스트나 JSON으로 값을 리턴할 때이다. XML로 리턴할 때는 text/xml.

아래는 Ajax 요청을 처리하기 위해서 만들어본 간단한 Encoding Filter 이다.

package ajax.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 어플리케이션 전체에 적용되는 필터이다.
 *
 * <ul>
 * <li>encoding 파라미터 : encoding 파라미터를 설정하면 request 객체에
 * setCharacterEncoding(encoding)을 실행한다.</li>
 * <li>ajaxFlag 파라미터 : Ajax요청임을 나타내는 HTTP 파라미터 이름이다. ajaxFilter로 지정한 HTTP 파라미터의
 * 값이 true 로 설정되면 인코딩을 무조건 UTF-8로 설정한다.</li>
 * </ul>
 *
 * @author 손권남(kwon37xi@yahoo.co.kr)
 *
 */
public class EncodingFilter implements Filter {

    private Log log = LogFactory.getLog(this.getClass());

    /** HTTP 요청 문자 인코딩 */
    private String encoding = null;

    /** Ajax 요청임을 나타내는 플래그 파라미터 이름 */
    private String ajaxFlag = null;

    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {

        if (ajaxFlag != null
                && "true".equals(((HttpServletRequest) request)
                        .getHeader(ajaxFlag))) {
            // Ajax 처리 요청일 경우 무조건 UTF-8 지정.
            request.setCharacterEncoding("UTF-8");
            if (log.isDebugEnabled()) {
                log.debug("요청 헤더에 " + ajaxFlag + "가 "
                        + ((HttpServletRequest) request).getHeader(ajaxFlag)
                        + "로 설정되어 있어 문자 인코딩에  UTF-8을 사용합니다.");
            }
        } else if (encoding != null) {
            // Ajax 플래그가 true가 아니면, 기본적인 인코딩을 적용한다.
            request.setCharacterEncoding(encoding);
            if (log.isDebugEnabled()) {
                log.debug("문자 인코딩에 " + encoding + "을 사용합니다.");
            }
        }

        chain.doFilter(request, response);
    }

    public void init(FilterConfig config) throws ServletException {
        encoding = config.getInitParameter("encoding");

        ajaxFlag = config.getInitParameter("ajaxFlag");

        if (log.isDebugEnabled()) {
            log.info("encoding : " + encoding + ", ajaxFlag : " + ajaxFlag);
        }
    }

    public void destroy() {
    }
}

이 필터를 적용하고서, web.xml에 다음 내용을 추가하면 필터가 작동한다.
<filter>
    <description>이중 인코딩 필터</description>
    <filter-name>EncodingFilter</filter-name>
    <filter-class>ajax.filter.EncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>euc-kr</param-value>
    </init-param>
    <init-param>
        <param-name>ajaxFlag</param-name>
        <param-value>Ajax</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>EncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

여 기 내용을 보면, 기본적인 인코딩은 EUC-KR이고, 요청 헤더에 "Ajax" 헤더의 값이 "true"일 경우에는 강제로 UTF-8을 지정하라고 한 것이다. "ajaxFlag"의 값을 변경하면 헤더의 키을 "Ajax"가 아닌 다른 값으로도 지정할 수 있다. 하지만 아무튼 해당 헤더의 값을 "true"로 지정하면 Ajax로 인식하게 되는 것이다.

이를 위해서는 XMLHttpRequest에도 한가지 처리를 더 보태야 한다.
2007/07/31 11:50 2007/07/31 11:50
이 글에는 트랙백을 보낼 수 없습니다

The previous four chapters of this book gave a broad overview of Java's architecture. They showed how the Java virtual machine fits into the overall architecture relative to other components such as the language and API. The remainder of this book will focus more narrowly on the Java virtual machine. This chapter gives an overview of the Java virtual machine's internal architecture.

The Java virtual machine is called "virtual" because it is an abstract computer defined by a specification. To run a Java program, you need a concrete implementation of the abstract specification. This chapter describes primarily the abstract specification of the Java virtual machine. To illustrate the abstract definition of certain features, however, this chapter also discusses various ways in which those features could be implemented.

What is a Java Virtual Machine?

To understand the Java virtual machine you must first be aware that you may be talking about any of three different things when you say "Java virtual machine." You may be speaking of:

  • the abstract specification,
  • a concrete implementation, or
  • a runtime instance.
The abstract specification is a concept, described in detail in the book: The Java Virtual Machine Specification, by Tim Lindholm and Frank Yellin. Concrete implementations, which exist on many platforms and come from many vendors, are either all software or a combination of hardware and software. A runtime instance hosts a single running Java application.

Each Java application runs inside a runtime instance of some concrete implementation of the abstract specification of the Java virtual machine. In this book, the term "Java virtual machine" is used in all three of these senses. Where the intended sense is not clear from the context, one of the terms "specification," "implementation," or "instance" is added to the term "Java virtual machine".

The Lifetime of a Java Virtual Machine

A runtime instance of the Java virtual machine has a clear mission in life: to run one Java application. When a Java application starts, a runtime instance is born. When the application completes, the instance dies. If you start three Java applications at the same time, on the same computer, using the same concrete implementation, you'll get three Java virtual machine instances. Each Java application runs inside its own Java virtual machine.

A Java virtual machine instance starts running its solitary application by invoking the main() method of some initial class. The main() method must be public, static, return void, and accept one parameter: a String array. Any class with such a main() method can be used as the starting point for a Java application.

For example, consider an application that prints out its command line arguments:

// On CD-ROM in file jvm/ex1/Echo.java
class Echo {

    public static void main(String[] args) {
        int len = args.length;
        for (int i = 0; i < len; ++i) {
            System.out.print(args[i] + " ");
        }
        System.out.println();
    }
}

You must in some implementation-dependent way give a Java virtual machine the name of the initial class that has the main() method that will start the entire application. One real world example of a Java virtual machine implementation is the java program from Sun's Java 2 SDK. If you wanted to run the Echo application using Sun's java on Window98, for example, you would type in a command such as:

java Echo Greetings, Planet.

The first word in the command, "java," indicates that the Java virtual machine from Sun's Java 2 SDK should be run by the operating system. The second word, "Echo," is the name of the initial class. Echo must have a public static method named main() that returns void and takes a String array as its only parameter. The subsequent words, "Greetings, Planet.," are the command line arguments for the application. These are passed to the main() method in the String array in the order in which they appear on the command line. So, for the previous example, the contents of the String array passed to main in Echo are: arg[0] is "Greetings," arg[1] is "Planet."

The main() method of an application's initial class serves as the starting point for that application's initial thread. The initial thread can in turn fire off other threads.

Inside the Java virtual machine, threads come in two flavors: daemon and non- daemon. A daemon thread is ordinarily a thread used by the virtual machine itself, such as a thread that performs garbage collection. The application, however, can mark any threads it creates as daemon threads. The initial thread of an application--the one that begins at main()--is a non- daemon thread.

A Java application continues to execute (the virtual machine instance continues to live) as long as any non-daemon threads are still running. When all non-daemon threads of a Java application terminate, the virtual machine instance will exit. If permitted by the security manager, the application can also cause its own demise by invoking the exit() method of class Runtime or System.

In the Echo application previous, the main() method doesn't invoke any other threads. After it prints out the command line arguments, main() returns. This terminates the application's only non-daemon thread, which causes the virtual machine instance to exit.

The Architecture of the Java Virtual Machine

In the Java virtual machine specification, the behavior of a virtual machine instance is described in terms of subsystems, memory areas, data types, and instructions. These components describe an abstract inner architecture for the abstract Java virtual machine. The purpose of these components is not so much to dictate an inner architecture for implementations. It is more to provide a way to strictly define the external behavior of implementations. The specification defines the required behavior of any Java virtual machine implementation in terms of these abstract components and their interactions.

Figure 5-1 shows a block diagram of the Java virtual machine that includes the major subsystems and memory areas described in the specification. As mentioned in previous chapters, each Java virtual machine has a class loader subsystem: a mechanism for loading types (classes and interfaces) given fully qualified names. Each Java virtual machine also has an execution engine: a mechanism responsible for executing the instructions contained in the methods of loaded classes.



Figure 5-1. The internal architecture of the Java virtual machine.

When a Java virtual machine runs a program, it needs memory to store many things, including bytecodes and other information it extracts from loaded class files, objects the program instantiates, parameters to methods, return values, local variables, and intermediate results of computations. The Java virtual machine organizes the memory it needs to execute a program into several runtime data areas.

Although the same runtime data areas exist in some form in every Java virtual machine implementation, their specification is quite abstract. Many decisions about the structural details of the runtime data areas are left to the designers of individual implementations.

Different implementations of the virtual machine can have very different memory constraints. Some implementations may have a lot of memory in which to work, others may have very little. Some implementations may be able to take advantage of virtual memory, others may not. The abstract nature of the specification of the runtime data areas helps make it easier to implement the Java virtual machine on a wide variety of computers and devices.

Some runtime data areas are shared among all of an application's threads and others are unique to individual threads. Each instance of the Java virtual machine has one method area and one heap. These areas are shared by all threads running inside the virtual machine. When the virtual machine loads a class file, it parses information about a type from the binary data contained in the class file. It places this type information into the method area. As the program runs, the virtual machine places all objects the program instantiates onto the heap. See Figure 5-2 for a graphical depiction of these memory areas.



Figure 5-2. Runtime data areas shared among all threads.

As each new thread comes into existence, it gets its own pc register (program counter) and Java stack. If the thread is executing a Java method (not a native method), the value of the pc register indicates the next instruction to execute. A thread's Java stack stores the state of Java (not native) method invocations for the thread. The state of a Java method invocation includes its local variables, the parameters with which it was invoked, its return value (if any), and intermediate calculations. The state of native method invocations is stored in an implementation-dependent way in native method stacks, as well as possibly in registers or other implementation-dependent memory areas.

The Java stack is composed of stack frames (or frames). A stack frame contains the state of one Java method invocation. When a thread invokes a method, the Java virtual machine pushes a new frame onto that thread's Java stack. When the method completes, the virtual machine pops and discards the frame for that method.

The Java virtual machine has no registers to hold intermediate data values. The instruction set uses the Java stack for storage of intermediate data values. This approach was taken by Java's designers to keep the Java virtual machine's instruction set compact and to facilitate implementation on architectures with few or irregular general purpose registers. In addition, the stack-based architecture of the Java virtual machine's instruction set facilitates the code optimization work done by just-in-time and dynamic compilers that operate at run-time in some virtual machine implementations.

See Figure 5-3 for a graphical depiction of the memory areas the Java virtual machine creates for each thread. These areas are private to the owning thread. No thread can access the pc register or Java stack of another thread.



Figure 5-3. Runtime data areas exclusive to each thread.

Figure 5-3 shows a snapshot of a virtual machine instance in which three threads are executing. At the instant of the snapshot, threads one and two are executing Java methods. Thread three is executing a native method.

In Figure 5-3, as in all graphical depictions of the Java stack in this book, the stacks are shown growing downwards. The "top" of each stack is shown at the bottom of the figure. Stack frames for currently executing methods are shown in a lighter shade. For threads that are currently executing a Java method, the pc register indicates the next instruction to execute. In Figure 5-3, such pc registers (the ones for threads one and two) are shown in a lighter shade. Because thread three is currently executing a native method, the contents of its pc register--the one shown in dark gray--is undefined.

Data Types

The Java virtual machine computes by performing operations on certain types of data. Both the data types and operations are strictly defined by the Java virtual machine specification. The data types can be divided into a set of primitive types and a reference type. Variables of the primitive types hold primitive values, and variables of the reference type hold reference values. Reference values refer to objects, but are not objects themselves. Primitive values, by contrast, do not refer to anything. They are the actual data themselves. You can see a graphical depiction of the Java virtual machine's families of data types in Figure 5-4.



Figure 5-4. Data types of the Java virtual machine.

All the primitive types of the Java programming language are primitive types of the Java virtual machine. Although boolean qualifies as a primitive type of the Java virtual machine, the instruction set has very limited support for it. When a compiler translates Java source code into bytecodes, it uses ints or bytes to represent booleans. In the Java virtual machine, false is represented by integer zero and true by any non-zero integer. Operations involving boolean values use ints. Arrays of boolean are accessed as arrays of byte, though they may be represented on the heap as arrays of byte or as bit fields.

The primitive types of the Java programming language other than boolean form the numeric types of the Java virtual machine. The numeric types are divided between the integral types: byte, short, int, long, and char, and the floating- point types: float and double. As with the Java programming language, the primitive types of the Java virtual machine have the same range everywhere. A long in the Java virtual machine always acts like a 64-bit signed twos complement number, independent of the underlying host platform.

The Java virtual machine works with one other primitive type that is unavailable to the Java programmer: the returnAddress type. This primitive type is used to implement finally clauses of Java programs. The use of the returnAddress type is described in detail in Chapter 18, "Finally Clauses."

The reference type of the Java virtual machine is cleverly named reference. Values of type reference come in three flavors: the class type, the interface type, and the array type. All three types have values that are references to dynamically created objects. The class type's values are references to class instances. The array type's values are references to arrays, which are full-fledged objects in the Java virtual machine. The interface type's values are references to class instances that implement an interface. One other reference value is the null value, which indicates the reference variable doesn't refer to any object.

The Java virtual machine specification defines the range of values for each of the data types, but does not define their sizes. The number of bits used to store each data type value is a decision of the designers of individual implementations. The ranges of the Java virtual machines data type's are shown in Table 5-1. More information on the floating point ranges is given in Chapter 14, "Floating Point Arithmetic."

Type Range
byte 8-bit signed two's complement integer (-27 to 27 - 1, inclusive)
short 16-bit signed two's complement integer (-215 to 215 - 1, inclusive)
int 32-bit signed two's complement integer (-231 to 231 - 1, inclusive)
long 64-bit signed two's complement integer (-263 to 263 - 1, inclusive)
char 16-bit unsigned Unicode character (0 to 216 - 1, inclusive)
float 32-bit IEEE 754 single-precision float
double 64-bit IEEE 754 double-precision float
returnAddress address of an opcode within the same method
reference reference to an object on the heap, or null

Table 5-1. Ranges of the Java virtual machine's data types

Word Size

The basic unit of size for data values in the Java virtual machine is the word--a fixed size chosen by the designer of each Java virtual machine implementation. The word size must be large enough to hold a value of type byte, short, int, char, float, returnAddress, or reference. Two words must be large enough to hold a value of type long or double. An implementation designer must therefore choose a word size that is at least 32 bits, but otherwise can pick whatever word size will yield the most efficient implementation. The word size is often chosen to be the size of a native pointer on the host platform.

The specification of many of the Java virtual machine's runtime data areas are based upon this abstract concept of a word. For example, two sections of a Java stack frame--the local variables and operand stack-- are defined in terms of words. These areas can contain values of any of the virtual machine's data types. When placed into the local variables or operand stack, a value occupies either one or two words.

As they run, Java programs cannot determine the word size of their host virtual machine implementation. The word size does not affect the behavior of a program. It is only an internal attribute of a virtual machine implementation.

The Class Loader Subsystem

The part of a Java virtual machine implementation that takes care of finding and loading types is the class loader subsystem. Chapter 1, "Introduction to Java's Architecture," gives an overview of this subsystem. Chapter 3, "Security," shows how the subsystem fits into Java's security model. This chapter describes the class loader subsystem in more detail and show how it relates to the other components of the virtual machine's internal architecture.

As mentioned in Chapter 1, the Java virtual machine contains two kinds of class loaders: a bootstrap class loader and user-defined class loaders. The bootstrap class loader is a part of the virtual machine implementation, and user-defined class loaders are part of the running Java application. Classes loaded by different class loaders are placed into separate name spaces inside the Java virtual machine.

The class loader subsystem involves many other parts of the Java virtual machine and several classes from the java.lang library. For example, user-defined class loaders are regular Java objects whose class descends from java.lang.ClassLoader. The methods of class ClassLoader allow Java applications to access the virtual machine's class loading machinery. Also, for every type a Java virtual machine loads, it creates an instance of class java.lang.Class to represent that type. Like all objects, user-defined class loaders and instances of class Class reside on the heap. Data for loaded types resides in the method area.

Loading, Linking and Initialization

The class loader subsystem is responsible for more than just locating and importing the binary data for classes. It must also verify the correctness of imported classes, allocate and initialize memory for class variables, and assist in the resolution of symbolic references. These activities are performed in a strict order:

  1. Loading: finding and importing the binary data for a type
  2. Linking: performing verification, preparation, and (optionally) resolution
    1. Verification: ensuring the correctness of the imported type
    2. Preparation: allocating memory for class variables and initializing the memory to default values
    3. Resolution: transforming symbolic references from the type into direct references.
  3. Initialization: invoking Java code that initializes class variables to their proper starting values.

The details of these processes are given Chapter 7, "The Lifetime of a Type."

The Bootstrap Class Loader

Java virtual machine implementations must be able to recognize and load classes and interfaces stored in binary files that conform to the Java class file format. An implementation is free to recognize other binary forms besides class files, but it must recognize class files.

Every Java virtual machine implementation has a bootstrap class loader, which knows how to load trusted classes, including the classes of the Java API. The Java virtual machine specification doesn't define how the bootstrap loader should locate classes. That is another decision the specification leaves to implementation designers.

Given a fully qualified type name, the bootstrap class loader must in some way attempt to produce the data that defines the type. One common approach is demonstrated by the Java virtual machine implementation in Sun's 1.1 JDK on Windows98. This implementation searches a user-defined directory path stored in an environment variable named CLASSPATH. The bootstrap loader looks in each directory, in the order the directories appear in the CLASSPATH, until it finds a file with the appropriate name: the type's simple name plus ".class". Unless the type is part of the unnamed package, the bootstrap loader expects the file to be in a subdirectory of one the directories in the CLASSPATH. The path name of the subdirectory is built from the package name of the type. For example, if the bootstrap class loader is searching for class java.lang.Object, it will look for Object.class in the java\lang subdirectory of each CLASSPATH directory.

In 1.2, the bootstrap class loader of Sun's Java 2 SDK only looks in the directory in which the system classes (the class files of the Java API) were installed. The bootstrap class loader of the implementation of the Java virtual machine from Sun's Java 2 SDK does not look on the CLASSPATH. In Sun's Java 2 SDK virtual machine, searching the class path is the job of the system class loader, a user-defined class loader that is created automatically when the virtual machine starts up. More information on the class loading scheme of Sun's Java 2 SDK is given in Chapter 8, "The Linking Model."

User-Defined Class Loaders

Although user-defined class loaders themselves are part of the Java application, four of the methods in class ClassLoader are gateways into the Java virtual machine:

// Four of the methods declared in class java.lang.ClassLoader:
protected final Class defineClass(String name, byte data[],
    int offset, int length);
protected final Class defineClass(String name, byte data[],
    int offset, int length, ProtectionDomain protectionDomain);
protected final Class findSystemClass(String name);
protected final void resolveClass(Class c);

Any Java virtual machine implementation must take care to connect these methods of class ClassLoader to the internal class loader subsystem.

The two overloaded defineClass() methods accept a byte array, data[], as input. Starting at position offset in the array and continuing for length bytes, class ClassLoader expects binary data conforming to the Java class file format--binary data that represents a new type for the running application -- with the fully qualified name specified in name. The type is assigned to either a default protection domain, if the first version of defineClass() is used, or to the protection domain object referenced by the protectionDomain parameter. Every Java virtual machine implementation must make sure the defineClass() method of class ClassLoader can cause a new type to be imported into the method area.

The findSystemClass() method accepts a String representing a fully qualified name of a type. When a user-defined class loader invokes this method in version 1.0 and 1.1, it is requesting that the virtual machine attempt to load the named type via its bootstrap class loader. If the bootstrap class loader has already loaded or successfully loads the type, it returns a reference to the Class object representing the type. If it can't locate the binary data for the type, it throws ClassNotFoundException. In version 1.2, the findSystemClass() method attempts to load the requested type from the system class loader. Every Java virtual machine implementation must make sure the findSystemClass() method can invoke the bootstrap (if version 1.0 or 1.1) or system (if version 1.2 or later) class loader in this way.

The resolveClass() method accepts a reference to a Class instance. This method causes the type represented by the Class instance to be linked (if it hasn't already been linked). The defineClass() method, described previous, only takes care of loading. (See the previous section, "Loading, Linking, and Initialization" for definitions of these terms.) When defineClass() returns a Class instance, the binary file for the type has definitely been located and imported into the method area, but not necessarily linked and initialized. Java virtual machine implementations make sure the resolveClass() method of class ClassLoader can cause the class loader subsystem to perform linking.

The details of how a Java virtual machine performs class loading, linking, and initialization, with user- defined class loaders is given in Chapter 8, "The Linking Model."

Name Spaces

As mentioned in Chapter 3, "Security," each class loader maintains its own name space populated by the types it has loaded. Because each class loader has its own name space, a single Java application can load multiple types with the same fully qualified name. A type's fully qualified name, therefore, is not always enough to uniquely identify it inside a Java virtual machine instance. If multiple types of that same name have been loaded into different name spaces, the identity of the class loader that loaded the type (the identity of the name space it is in) will also be needed to uniquely identify that type.

Name spaces arise inside a Java virtual machine instance as a result of the process of resolution. As part of the data for each loaded type, the Java virtual machine keeps track of the class loader that imported the type. When the virtual machine needs to resolve a symbolic reference from one class to another, it requests the referenced class from the same class loader that loaded the referencing class. This process is described in detail in Chapter 8, "The Linking Model."

The Method Area

Inside a Java virtual machine instance, information about loaded types is stored in a logical area of memory called the method area. When the Java virtual machine loads a type, it uses a class loader to locate the appropriate class file. The class loader reads in the class file--a linear stream of binary data--and passes it to the virtual machine. The virtual machine extracts information about the type from the binary data and stores the information in the method area. Memory for class (static) variables declared in the class is also taken from the method area.

The manner in which a Java virtual machine implementation represents type information internally is a decision of the implementation designer. For example, multi-byte quantities in class files are stored in big- endian (most significant byte first) order. When the data is imported into the method area, however, a virtual machine can store the data in any manner. If an implementation sits on top of a little-endian processor, the designers may decide to store multi-byte values in the method area in little-endian order.

The virtual machine will search through and use the type information stored in the method area as it executes the application it is hosting. Designers must attempt to devise data structures that will facilitate speedy execution of the Java application, but must also think of compactness. If designing an implementation that will operate under low memory constraints, designers may decide to trade off some execution speed in favor of compactness. If designing an implementation that will run on a virtual memory system, on the other hand, designers may decide to store redundant information in the method area to facilitate execution speed. (If the underlying host doesn't offer virtual memory, but does offer a hard disk, designers could create their own virtual memory system as part of their implementation.) Designers can choose whatever data structures and organization they feel optimize their implementations performance, in the context of its requirements.

All threads share the same method area, so access to the method area's data structures must be designed to be thread-safe. If two threads are attempting to find a class named Lava, for example, and Lava has not yet been loaded, only one thread should be allowed to load it while the other one waits.

The size of the method area need not be fixed. As the Java application runs, the virtual machine can expand and contract the method area to fit the application's needs. Also, the memory of the method area need not be contiguous. It could be allocated on a heap--even on the virtual machine's own heap. Implementations may allow users or programmers to specify an initial size for the method area, as well as a maximum or minimum size.

The method area can also be garbage collected. Because Java programs can be dynamically extended via user-defined class loaders, classes can become "unreferenced" by the application. If a class becomes unreferenced, a Java virtual machine can unload the class (garbage collect it) to keep the memory occupied by the method area at a minimum. The unloading of classes--including the conditions under which a class can become "unreferenced"--is described in Chapter 7, "The Lifetime of a Type."

Type Information

For each type it loads, a Java virtual machine must store the following kinds of information in the method area:

  • The fully qualified name of the type
  • The fully qualified name of the type's direct superclass (unless the type is an interface or class java.lang.Object, neither of which have a superclass)
  • Whether or not the type is a class or an interface
  • The type's modifiers ( some subset of` public, abstract, final)
  • An ordered list of the fully qualified names of any direct superinterfaces

Inside the Java class file and Java virtual machine, type names are always stored as fully qualified names. In Java source code, a fully qualified name is the name of a type's package, plus a dot, plus the type's simple name. For example, the fully qualified name of class Object in package java.lang is java.lang.Object. In class files, the dots are replaced by slashes, as in java/lang/Object. In the method area, fully qualified names can be represented in whatever form and data structures a designer chooses.

In addition to the basic type information listed previously, the virtual machine must also store for each loaded type:

  • The constant pool for the type
  • Field information
  • Method information
  • All class (static) variables declared in the type, except constants
  • A reference to class ClassLoader
  • A reference to class Class

This data is described in the following sections.

The Constant Pool

For each type it loads, a Java virtual machine must store a constant pool. A constant pool is an ordered set of constants used by the type, including literals (string, integer, and floating point constants) and symbolic references to types, fields, and methods. Entries in the constant pool are referenced by index, much like the elements of an array. Because it holds symbolic references to all types, fields, and methods used by a type, the constant pool plays a central role in the dynamic linking of Java programs. The constant pool is described in more detail later in this chapter and in Chapter 6, "The Java Class File."

Field Information

For each field declared in the type, the following information must be stored in the method area. In addition to the information for each field, the order in which the fields are declared by the class or interface must also be recorded. Here's the list for fields:

  • The field's name
  • The field's type
  • The field's modifiers (some subset of public, private, protected, static, final, volatile, transient)

Method Information

For each method declared in the type, the following information must be stored in the method area. As with fields, the order in which the methods are declared by the class or interface must be recorded as well as the data. Here's the list:

  • The method's name
  • The method's return type (or void)
  • The number and types (in order) of the method's parameters
  • The method's modifiers (some subset of public, private, protected, static, final, synchronized, native, abstract)

In addition to the items listed previously, the following information must also be stored with each method that is not abstract or native:

  • The method's bytecodes
  • The sizes of the operand stack and local variables sections of the method's stack frame (these are described in a later section of this chapter)
  • An exception table (this is described in Chapter 17, "Exceptions")

Class Variables

Class variables are shared among all instances of a class and can be accessed even in the absence of any instance. These variables are associated with the class--not with instances of the class--so they are logically part of the class data in the method area. Before a Java virtual machine uses a class, it must allocate memory from the method area for each non-final class variable declared in the class.

Constants (class variables declared final) are not treated in the same way as non-final class variables. Every type that uses a final class variable gets a copy of the constant value in its own constant pool. As part of the constant pool, final class variables are stored in the method area--just like non-final class variables. But whereas non-final class variables are stored as part of the data for the type that declares them, final class variables are stored as part of the data for any type that uses them. This special treatment of constants is explained in more detail in Chapter 6, "The Java Class File."

A Reference to Class ClassLoader

For each type it loads, a Java virtual machine must keep track of whether or not the type was loaded via the bootstrap class loader or a user-defined class loader. For those types loaded via a user-defined class loader, the virtual machine must store a reference to the user-defined class loader that loaded the type. This information is stored as part of the type's data in the method area.

The virtual machine uses this information during dynamic linking. When one type refers to another type, the virtual machine requests the referenced type from the same class loader that loaded the referencing type. This process of dynamic linking is also central to the way the virtual machine forms separate name spaces. To be able to properly perform dynamic linking and maintain multiple name spaces, the virtual machine needs to know what class loader loaded each type in its method area. The details of dynamic linking and name spaces are given in Chapter 8, "The Linking Model."

A Reference to Class Class

An instance of class java.lang.Class is created by the Java virtual machine for every type it loads. The virtual machine must in some way associate a reference to the Class instance for a type with the type's data in the method area.

Your Java programs can obtain and use references to Class objects. One static method in class Class, allows you to get a reference to the Class instance for any loaded class:

// A method declared in class java.lang.Class:
public static Class forName(String className);

If you invoke forName("java.lang.Object"), for example, you will get a reference to the Class object that represents java.lang.Object. If you invoke forName("java.util.Enumeration"), you will get a reference to the Class object that represents the Enumeration interface from the java.util package. You can use forName() to get a Class reference for any loaded type from any package, so long as the type can be (or already has been) loaded into the current name space. If the virtual machine is unable to load the requested type into the current name space, forName() will throw ClassNotFoundException.

An alternative way to get a Class reference is to invoke getClass() on any object reference. This method is inherited by every object from class Object itself:

// A method declared in class java.lang.Object:
public final Class getClass();

If you have a reference to an object of class java.lang.Integer, for example, you could get the Class object for java.lang.Integer simply by invoking getClass() on your reference to the Integer object.

Given a reference to a Class object, you can find out information about the type by invoking methods declared in class Class. If you look at these methods, you will quickly realize that class Class gives the running application access to the information stored in the method area. Here are some of the methods declared in class Class:

// Some of the methods declared in class java.lang.Class:
public String getName();
public Class getSuperClass();
public boolean isInterface();
public Class[] getInterfaces();
public ClassLoader getClassLoader();

These methods just return information about a loaded type. getName() returns the fully qualified name of the type. getSuperClass() returns the Class instance for the type's direct superclass. If the type is class java.lang.Object or an interface, none of which have a superclass, getSuperClass() returns null. isInterface() returns true if the Class object describes an interface, false if it describes a class. getInterfaces() returns an array of Class objects, one for each direct superinterface. The superinterfaces appear in the array in the order they are declared as superinterfaces by the type. If the type has no direct superinterfaces, getInterfaces() returns an array of length zero. getClassLoader() returns a reference to the ClassLoader object that loaded this type, or null if the type was loaded by the bootstrap class loader. All this information comes straight out of the method area.

Method Tables

The type information stored in the method area must be organized to be quickly accessible. In addition to the raw type information listed previously, implementations may include other data structures that speed up access to the raw data. One example of such a data structure is a method table. For each non-abstract class a Java virtual machine loads, it could generate a method table and include it as part of the class information it stores in the method area. A method table is an array of direct references to all the instance methods that may be invoked on a class instance, including instance methods inherited from superclasses. (A method table isn't helpful in the case of abstract classes or interfaces, because the program will never instantiate these.) A method table allows a virtual machine to quickly locate an instance method invoked on an object. Method tables are described in detail in Chapter 8, "The Linking Model."

An Example of Method Area Use

As an example of how the Java virtual machine uses the information it stores in the method area, consider these classes:

// On CD-ROM in file jvm/ex2/Lava.java
class Lava {

    private int speed = 5; // 5 kilometers per hour

    void flow() {
    }
}

// On CD-ROM in file jvm/ex2/Volcano.java
class Volcano {

    public static void main(String[] args) {
        Lava lava = new Lava();
        lava.flow();
    }
}

The following paragraphs describe how an implementation might execute the first instruction in the bytecodes for the main() method of the Volcano application. Different implementations of the Java virtual machine can operate in very different ways. The following description illustrates one way--but not the only way--a Java virtual machine could execute the first instruction of Volcano's main() method.

To run the Volcano application, you give the name "Volcano" to a Java virtual machine in an implementation-dependent manner. Given the name Volcano, the virtual machine finds and reads in file Volcano.class. It extracts the definition of class Volcano from the binary data in the imported class file and places the information into the method area. The virtual machine then invokes the main() method, by interpreting the bytecodes stored in the method area. As the virtual machine executes main(), it maintains a pointer to the constant pool (a data structure in the method area) for the current class (class Volcano).

Note that this Java virtual machine has already begun to execute the bytecodes for main() in class Volcano even though it hasn't yet loaded class Lava. Like many (probably most) implementations of the Java virtual machine, this implementation doesn't wait until all classes used by the application are loaded before it begins executing main(). It loads classes only as it needs them.

main()'s first instruction tells the Java virtual machine to allocate enough memory for the class listed in constant pool entry one. The virtual machine uses its pointer into Volcano's constant pool to look up entry one and finds a symbolic reference to class Lava. It checks the method area to see if Lava has already been loaded.

The symbolic reference is just a string giving the class's fully qualified name: "Lava". Here you can see that the method area must be organized so a class can be located--as quickly as possible--given only the class's fully qualified name. Implementation designers can choose whatever algorithm and data structures best fit their needs--a hash table, a search tree, anything. This same mechanism can be used by the static forName() method of class Class, which returns a Class reference given a fully qualified name.

When the virtual machine discovers that it hasn't yet loaded a class named "Lava," it proceeds to find and read in file Lava.class. It extracts the definition of class Lava from the imported binary data and places the information into the method area.

The Java virtual machine then replaces the symbolic reference in Volcano's constant pool entry one, which is just the string "Lava", with a pointer to the class data for Lava. If the virtual machine ever has to use Volcano's constant pool entry one again, it won't have to go through the relatively slow process of searching through the method area for class Lava given only a symbolic reference, the string "Lava". It can just use the pointer to more quickly access the class data for Lava. This process of replacing symbolic references with direct references (in this case, a native pointer) is called constant pool resolution. The symbolic reference is resolved into a direct reference by searching through the method area until the referenced entity is found, loading new classes if necessary.

Finally, the virtual machine is ready to actually allocate memory for a new Lava object. Once again, the virtual machine consults the information stored in the method area. It uses the pointer (which was just put into Volcano's constant pool entry one) to the Lava data (which was just imported into the method area) to find out how much heap space is required by a Lava object.

A Java virtual machine can always determine the amount of memory required to represent an object by looking into the class data stored in the method area. The actual amount of heap space required by a particular object, however, is implementation-dependent. The internal representation of objects inside a Java virtual machine is another decision of implementation designers. Object representation is discussed in more detail later in this chapter.

Once the Java virtual machine has determined the amount of heap space required by a Lava object, it allocates that space on the heap and initializes the instance variable speed to zero, its default initial value. If class Lava's superclass, Object, has any instance variables, those are also initialized to default initial values. (The details of initialization of both classes and objects are given in Chapter 7, "The Lifetime of a Type.")

The first instruction of main() completes by pushing a reference to the new Lava object onto the stack. A later instruction will use the reference to invoke Java code that initializes the speed variable to its proper initial value, five. Another instruction will use the reference to invoke the flow() method on the referenced Lava object.

The Heap

Whenever a class instance or array is created in a running Java application, the memory for the new object is allocated from a single heap. As there is only one heap inside a Java virtual machine instance, all threads share it. Because a Java application runs inside its "own" exclusive Java virtual machine instance, there is a separate heap for every individual running application. There is no way two different Java applications could trample on each other's heap data. Two different threads of the same application, however, could trample on each other's heap data. This is why you must be concerned about proper synchronization of multi-threaded access to objects (heap data) in your Java programs.

The Java virtual machine has an instruction that allocates memory on the heap for a new object, but has no instruction for freeing that memory. Just as you can't explicitly free an object in Java source code, you can't explicitly free an object in Java bytecodes. The virtual machine itself is responsible for deciding whether and when to free memory occupied by objects that are no longer referenced by the running application. Usually, a Java virtual machine implementation uses a garbage collector to manage the heap.

Garbage Collection

A garbage collector's primary function is to automatically reclaim the memory used by objects that are no longer referenced by the running application. It may also move objects as the application runs to reduce heap fragmentation.

A garbage collector is not strictly required by the Java virtual machine specification. The specification only requires that an implementation manage its own heap in some manner. For example, an implementation could simply have a fixed amount of heap space available and throw an OutOfMemory exception when that space fills up. While this implementation may not win many prizes, it does qualify as a Java virtual machine. The Java virtual machine specification does not say how much memory an implementation must make available to running programs. It does not say how an implementation must manage its heap. It says to implementation designers only that the program will be allocating memory from the heap, but not freeing it. It is up to designers to figure out how they want to deal with that fact.

No garbage collection technique is dictated by the Java virtual machine specification. Designers can use whatever techniques seem most appropriate given their goals, constraints, and talents. Because references to objects can exist in many places--Java Stacks, the heap, the method area, native method stacks--the choice of garbage collection technique heavily influences the design of an implementation's runtime data areas. Various garbage collection techniques are described in Chapter 9, "Garbage Collection."

As with the method area, the memory that makes up the heap need not be contiguous, and may be expanded and contracted as the running program progresses. An implementation's method area could, in fact, be implemented on top of its heap. In other words, when a virtual machine needs memory for a freshly loaded class, it could take that memory from the same heap on which objects reside. The same garbage collector that frees memory occupied by unreferenced objects could take care of finding and freeing (unloading) unreferenced classes. Implementations may allow users or programmers to specify an initial size for the heap, as well as a maximum and minimum size.

Object Representation

The Java virtual machine specification is silent on how objects should be represented on the heap. Object representation--an integral aspect of the overall design of the heap and garbage collector--is a decision of implementation designers

The primary data that must in some way be represented for each object is the instance variables declared in the object's class and all its superclasses. Given an object reference, the virtual machine must be able to quickly locate the instance data for the object. In addition, there must be some way to access an object's class data (stored in the method area) given a reference to the object. For this reason, the memory allocated for an object usually includes some kind of pointer into the method area.

One possible heap design divides the heap into two parts: a handle pool and an object pool. An object reference is a native pointer to a handle pool entry. A handle pool entry has two components: a pointer to instance data in the object pool and a pointer to class data in the method area. The advantage of this scheme is that it makes it easy for the virtual machine to combat heap fragmentation. When the virtual machine moves an object in the object pool, it need only update one pointer with the object's new address: the relevant pointer in the handle pool. The disadvantage of this approach is that every access to an object's instance data requires dereferencing two pointers. This approach to object representation is shown graphically in Figure 5-5. This kind of heap is demonstrated interactively by the HeapOfFish applet, described in Chapter 9, "Garbage Collection."



Figure 5-5. Splitting an object across a handle pool and object pool.

Another design makes an object reference a native pointer to a bundle of data that contains the object's instance data and a pointer to the object's class data. This approach requires dereferencing only one pointer to access an object's instance data, but makes moving objects more complicated. When the virtual machine moves an object to combat fragmentation of this kind of heap, it must update every reference to that object anywhere in the runtime data areas. This approach to object representation is shown graphically in Figure 5-6.



Figure 5-6. Keeping object data all in one place.

The virtual machine needs to get from an object reference to that object's class data for several reasons. When a running program attempts to cast an object reference to another type, the virtual machine must check to see if the type being cast to is the actual class of the referenced object or one of its supertypes. . It must perform the same kind of check when a program performs an instanceof operation. In either case, the virtual machine must look into the class data of the referenced object. When a program invokes an instance method, the virtual machine must perform dynamic binding: it must choose the method to invoke based not on the type of the reference but on the class of the object. To do this, it must once again have access to the class data given only a reference to the object.

No matter what object representation an implementation uses, it is likely that a method table is close at hand for each object. Method tables, because they speed up the invocation of instance methods, can play an important role in achieving good overall performance for a virtual machine implementation. Method tables are not required by the Java virtual machine specification and may not exist in all implementations. Implementations that have extremely low memory requirements, for instance, may not be able to afford the extra memory space method tables occupy. If an implementation does use method tables, however, an object's method table will likely be quickly accessible given just a reference to the object.

One way an implementation could connect a method table to an object reference is shown graphically in Figure 5-7. This figure shows that the pointer kept with the instance data for each object points to a special structure. The special structure has two components:

  • A pointer to the full the class data for the object
  • The method table for the object The method table is an array of pointers to the data for each instance method that can be invoked on objects of that class. The method data pointed to by method table includes:
  • The sizes of the operand stack and local variables sections of the method's stack
  • The method's bytecodes
  • An exception table

This gives the virtual machine enough information to invoke the method. The method table include pointers to data for methods declared explicitly in the object's class or inherited from superclasses. In other words, the pointers in the method table may point to methods defined in the object's class or any of its superclasses. More information on method tables is given in Chapter 8, "The Linking Model."



Figure 5-7. Keeping the method table close at hand.

If you are familiar with the inner workings of C++, you may recognize the method table as similar to the VTBL or virtual table of C++ objects. In C++, objects are represented by their instance data plus an array of pointers to any virtual functions that can be invoked on the object. This approach could also be taken by a Java virtual machine implementation. An implementation could include a copy of the method table for a class as part of the heap image for every instance of that class. This approach would consume more heap space than the approach shown in Figure 5-7, but might yield slightly better performance on a systems that enjoy large quantities of available memory.

One other kind of data that is not shown in Figures 5-5 and 5-6, but which is logically part of an object's data on the heap, is the object's lock. Each object in a Java virtual machine is associated with a lock (or mutex) that a program can use to coordinate multi-threaded access to the object. Only one thread at a time can "own" an object's lock. While a particular thread owns a particular object's lock, only that thread can access that object's instance variables. All other threads that attempt to access the object's variables have to wait until the owning thread releases the object's lock. If a thread requests a lock that is already owned by another thread, the requesting thread has to wait until the owning thread releases the lock. Once a thread owns a lock, it can request the same lock again multiple times, but then has to release the lock the same number of times before it is made available to other threads. If a thread requests a lock three times, for example, that thread will continue to own the lock until it has released it three times.

Many objects will go through their entire lifetimes without ever being locked by a thread. The data required to implement an object's lock is not needed unless the lock is actually requested by a thread. As a result, many implementations, such as the ones shown in Figure 5-5 and 5-6, may not include a pointer to "lock data" within the object itself. Such implementations must create the necessary data to represent a lock when the lock is requested for the first time. In this scheme, the virtual machine must associate the lock with the object in some indirect way, such as by placing the lock data into a search tree based on the object's address.

Along with data that implements a lock, every Java object is logically associated with data that implements a wait set. Whereas locks help threads to work independently on shared data without interfering with one another, wait sets help threads to cooperate with one another--to work together towards a common goal.

Wait sets are used in conjunction with wait and notify methods. Every class inherits from Object three "wait methods" (overloaded forms of a method named wait()) and two "notify methods" (notify() and notifyAll()). When a thread invokes a wait method on an object, the Java virtual machine suspends that thread and adds it to that object's wait set. When a thread invokes a notify method on an object, the virtual machine will at some future time wake up one or more threads from that object's wait set. As with the data that implements an object's lock, the data that implements an object's wait set is not needed unless a wait or notify method is actually invoked on the object. As a result, many implementations of the Java virtual machine may keep the wait set data separate from the actual object data. Such implementations could allocate the data needed to represent an object's wait set when a wait or notify method is first invoked on that object by the running application. For more information about locks and wait sets, see Chapter 20, "Thread Synchronization."

One last example of a type of data that may be included as part of the image of an object on the heap is any data needed by the garbage collector. The garbage collector must in some way keep track of which objects are referenced by the program. This task invariably requires data to be kept for each object on the heap. The kind of data required depends upon the garbage collection technique being used. For example, if an implementation uses a mark and sweep algorithm, it must be able to mark an object as referenced or unreferenced. For each unreferenced object, it may also need to indicate whether or not the object's finalizer has been run. As with thread locks, this data may be kept separate from the object image. Some garbage collection techniques only require this extra data while the garbage collector is actually running. A mark and sweep algorithm, for instance, could potentially use a separate bitmap for marking referenced and unreferenced objects. More detail on various garbage collection techniques, and the data that is required by each of them, is given in Chapter 9, "Garbage Collection."

In addition to data that a garbage collector uses to distinguish between reference and unreferenced objects, a garbage collector needs data to keep track of which objects on which it has already executed a finalizer. Garbage collectors must run the finalizer of any object whose class declares one before it reclaims the memory occupied by that object. The Java language specification states that a garbage collector will only execute an object's finalizer once, but allows that finalizer to "resurrect" the object: to make the object referenced again. When the object becomes unreferenced for a second time, the garbage collector must not finalize it again. Because most objects will likely not have a finalizer, and very few of those will resurrect their objects, this scenario of garbage collecting the same object twice will probably be extremely rare. As a result, the data used to keep track of objects that have already been finalized, though logically part of the data associated with an object, will likely not be part of the object representation on the heap. In most cases, garbage collectors will keep this information in a separate place. Chapter 9, "Garbage Collection," gives more information about finalization.

Array Representation

In Java, arrays are full-fledged objects. Like objects, arrays are always stored on the heap. Also like objects, implementation designers can decide how they want to represent arrays on the heap.

Arrays have a Class instance associated with their class, just like any other object. All arrays of the same dimension and type have the same class. The length of an array (or the lengths of each dimension of a multidimensional array) does not play any role in establishing the array's class. For example, an array of three ints has the same class as an array of three hundred ints. The length of an array is considered part of its instance data.

The name of an array's class has one open square bracket for each dimension plus a letter or string representing the array's type. For example, the class name for an array of ints is "[I". The class name for a three-dimensional array of bytes is "[[[B". The class name for a two-dimensional array of Objects is "[[Ljava.lang.Object". The full details of this naming convention for array classes is given in Chapter 6, "The Java Class File."

Multi-dimensional arrays are represented as arrays of arrays. A two dimensional array of ints, for example, would be represented by a one dimensional array of references to several one dimensional arrays of ints. This is shown graphically in Figure 5-8.



Figure 5-8. One possible heap representation for arrays.

The data that must be kept on the heap for each array is the array's length, the array data, and some kind of reference to the array's class data. Given a reference to an array, the virtual machine must be able to determine the array's length, to get and set its elements by index (checking to make sure the array bounds are not exceeded), and to invoke any methods declared by Object, the direct superclass of all arrays.

The Program Counter

Each thread of a running program has its own pc register, or program counter, which is created when the thread is started. The pc register is one word in size, so it can hold both a native pointer and a returnAddress. As a thread executes a Java method, the pc register contains the address of the current instruction being executed by the thread. An "address" can be a native pointer or an offset from the beginning of a method's bytecodes. If a thread is executing a native method, the value of the pc register is undefined.

The Java Stack

When a new thread is launched, the Java virtual machine creates a new Java stack for the thread. As mentioned earlier, a Java stack stores a thread's state in discrete frames. The Java virtual machine only performs two operations directly on Java Stacks: it pushes and pops frames.

The method that is currently being executed by a thread is the thread's current method. The stack frame for the current method is the current frame. The class in which the current method is defined is called the current class, and the current class's constant pool is the current constant pool. As it executes a method, the Java virtual machine keeps track of the current class and current constant pool. When the virtual machine encounters instructions that operate on data stored in the stack frame, it performs those operations on the current frame.

When a thread invokes a Java method, the virtual machine creates and pushes a new frame onto the thread's Java stack. This new frame then becomes the current frame. As the method executes, it uses the frame to store parameters, local variables, intermediate computations, and other data.

A method can complete in either of two ways. If a method completes by returning, it is said to have normal completion. If it completes by throwing an exception, it is said to have abrupt completion. When a method completes, whether normally or abruptly, the Java virtual machine pops and discards the method's stack frame. The frame for the previous method then becomes the current frame.

All the data on a thread's Java stack is private to that thread. There is no way for a thread to access or alter the Java stack of another thread. Because of this, you need never worry about synchronizing multi- threaded access to local variables in your Java programs. When a thread invokes a method, the method's local variables are stored in a frame on the invoking thread's Java stack. Only one thread can ever access those local variables: the thread that invoked the method.

Like the method area and heap, the Java stack and stack frames need not be contiguous in memory. Frames could be allocated on a contiguous stack, or they could be allocated on a heap, or some combination of both. The actual data structures used to represent the Java stack and stack frames is a decision of implementation designers. Implementations may allow users or programmers to specify an initial size for Java stacks, as well as a maximum or minimum size.

The Stack Frame

The stack frame has three parts: local variables, operand stack, and frame data. The sizes of the local variables and operand stack, which are measured in words, depend upon the needs of each individual method. These sizes are determined at compile time and included in the class file data for each method. The size of the frame data is implementation dependent.

When the Java virtual machine invokes a Java method, it checks the class data to determine the number of words required by the method in the local variables and operand stack. It creates a stack frame of the proper size for the method and pushes it onto the Java stack.

Local Variables

The local variables section of the Java stack frame is organized as a zero-based array of words. Instructions that use a value from the local variables section provide an index into the zero-based array. Values of type int, float, reference, and returnAddress occupy one entry in the local variables array. Values of type byte, short, and char are converted to int before being stored into the local variables. Values of type long and double occupy two consecutive entries in the array.

To refer to a long or double in the local variables, instructions provide the index of the first of the two consecutive entries occupied by the value. For example, if a long occupies array entries three and four, instructions would refer to that long by index three. All values in the local variables are word-aligned. Dual-entry longs and doubles can start at any index.

The local variables section contains a method's parameters and local variables. Compilers place the parameters into the local variable array first, in the order in which they are declared. Figure 5-9 shows the local variables section for the following two methods:

// On CD-ROM in file jvm/ex3/Example3a.java
class Example3a {

    public static int runClassMethod(int i, long l, float f,
        double d, Object o, byte b) {

        return 0;
    }

    public int runInstanceMethod(char c, double d, short s,
        boolean b) {

        return 0;
    }
}



Figure 5-9. Method parameters on the local variables section of a Java stack.

Note that Figure 5-9 shows that the first parameter in the local variables for runInstanceMethod() is of type reference, even though no such parameter appears in the source code. This is the hidden this reference passed to every instance method. Instance methods use this reference to access the instance data of the object upon which they were invoked. As you can see by looking at the local variables for runClassMethod() in Figure 5-9, class methods do not receive a hidden this. Class methods are not invoked on objects. You can't directly access a class's instance variables from a class method, because there is no instance associated with the method invocation.

Note also that types byte, short, char, and boolean in the source code become ints in the local variables. This is also true of the operand stack. As mentioned earlier, the boolean type is not supported directly by the Java virtual machine. The Java compiler always uses ints to represent boolean values in the local variables or operand stack. Data types byte, short, and char, however, are supported directly by the Java virtual machine. These can be stored on the heap as instance variables or array elements, or in the method area as class variables. When placed into local variables or the operand stack, however, values of type byte, short, and char are converted into ints. They are manipulated as ints while on the stack frame, then converted back into byte, short, or char when stored back into heap or method area.

Also note that Object o is passed as a reference to runClassMethod(). In Java, all objects are passed by reference. As all objects are stored on the heap, you will never find an image of an object in the local variables or operand stack, only object references.

Aside from a method's parameters, which compilers must place into the local variables array first and in order of declaration, Java compilers can arrange the local variables array as they wish. Compilers can place the method's local variables into the array in any order, and they can use the same array entry for more than one local variable. For example, if two local variables have limited scopes that don't overlap, such as the i and j local variables in Example3b, compilers are free to use the same array entry for both variables. During the first half of the method, before j comes into scope, entry zero could be used for i. During the second half of the method, after i has gone out of scope, entry zero could be used for j.

// On CD-ROM in file jvm/ex3/Example3b.java
class Example3b {

    public static void runtwoLoops() {

        for (int i = 0; i < 10; ++i) {
            System.out.println(i);
        }

        for (int j = 9; j >= 0; --j) {
            System.out.println(j);
        }
    }
}

As with all the other runtime memory areas, implementation designers can use whatever data structures they deem most appropriate to represent the local variables. The Java virtual machine specification does not indicate how longs and doubles should be split across the two array entries they occupy. Implementations that use a word size of 64 bits could, for example, store the entire long or double in the lower of the two consecutive entries, leaving the higher entry unused.

Operand Stack

Like the local variables, the operand stack is organized as an array of words. But unlike the local variables, which are accessed via array indices, the operand stack is accessed by pushing and popping values. If an instruction pushes a value onto the operand stack, a later instruction can pop and use that value.

The virtual machine stores the same data types in the operand stack that it stores in the local variables: int, long, float, double, reference, and returnType. It converts values of type byte, short, and char to int before pushing them onto the operand stack.

Other than the program counter, which can't be directly accessed by instructions, the Java virtual machine has no registers. The Java virtual machine is stack-based rather than register-based because its instructions take their operands from the operand stack rather than from registers. Instructions can also take operands from other places, such as immediately following the opcode (the byte representing the instruction) in the bytecode stream, or from the constant pool. The Java virtual machine instruction set's main focus of attention, however, is the operand stack.

The Java virtual machine uses the operand stack as a work space. Many instructions pop values from the operand stack, operate on them, and push the result. For example, the iadd instruction adds two integers by popping two ints off the top of the operand stack, adding them, and pushing the int result. Here is how a Java virtual machine would add two local variables that contain ints and store the int result in a third local variable:

iload_0    // push the int in local variable 0
iload_1    // push the int in local variable 1
iadd       // pop two ints, add them, push result
istore_2   // pop int, store into local variable 2

In this sequence of bytecodes, the first two instructions, iload_0 and iload_1, push the ints stored in local variable positions zero and one onto the operand stack. The iadd instruction pops those two int values, adds them, and pushes the int result back onto the operand stack. The fourth instruction, istore_2, pops the result of the add off the top of the operand stack and stores it into local variable position two. In Figure 5-10, you can see a graphical depiction of the state of the local variables and operand stack while executing these instructions. In this figure, unused slots of the local variables and operand stack are left blank.



Figure 5-10. Adding two local variables.

Frame Data

In addition to the local variables and operand stack, the Java stack frame includes data to support constant pool resolution, normal method return, and exception dispatch. This data is stored in the frame data portion of the Java stack frame.

Many instructions in the Java virtual machine's instruction set refer to entries in the constant pool. Some instructions merely push constant values of type int, long, float, double, or String from the constant pool onto the operand stack. Some instructions use constant pool entries to refer to classes or arrays to instantiate, fields to access, or methods to invoke. Other instructions determine whether a particular object is a descendant of a particular class or interface specified by a constant pool entry.

Whenever the Java virtual machine encounters any of the instructions that refer to an entry in the constant pool, it uses the frame data's pointer to the constant pool to access that information. As mentioned earlier, references to types, fields, and methods in the constant pool are initially symbolic. When the virtual machine looks up a constant pool entry that refers to a class, interface, field, or method, that reference may still be symbolic. If so, the virtual machine must resolve the reference at that time.

Aside from constant pool resolution, the frame data must assist the virtual machine in processing a normal or abrupt method completion. If a method completes normally (by returning), the virtual machine must restore the stack frame of the invoking method. It must set the pc register to point to the instruction in the invoking method that follows the instruction that invoked the completing method. If the completing method returns a value, the virtual machine must push that value onto the operand stack of the invoking method.

The frame data must also contain some kind of reference to the method's exception table, which the virtual machine uses to process any exceptions thrown during the course of execution of the method. An exception table, which is described in detail in Chapter 17, "Exceptions," defines ranges within the bytecodes of a method that are protected by catch clauses. Each entry in an exception table gives a starting and ending position of the range protected by a catch clause, an index into the constant pool that gives the exception class being caught, and a starting position of the catch clause's code.

When a method throws an exception, the Java virtual machine uses the exception table referred to by the frame data to determine how to handle the exception. If the virtual machine finds a matching catch clause in the method's exception table, it transfers control to the beginning of that catch clause. If the virtual machine doesn't find a matching catch clause, the method completes abruptly. The virtual machine uses the information in the frame data to restore the invoking method's frame. It then rethrows the same exception in the context of the invoking method.

In addition to data to support constant pool resolution, normal method return, and exception dispatch, the stack frame may also include other information that is implementation dependent, such as data to support debugging.

Possible Implementations of the Java Stack

Implementation designers can represent the Java stack in whatever way they wish. As mentioned earlier, one potential way to implement the stack is by allocating each frame separately from a heap. As an example of this approach, consider the following class:

// On CD-ROM in file jvm/ex3/Example3c.java
class Example3c {

    public static void addAndPrint() {
        double result = addTwoTypes(1, 88.88);
        System.out.println(result);
    }

    public static double addTwoTypes(int i, double d) {
        return i + d;
    }
}

Figure 5-11 shows three snapshots of the Java stack for a thread that invokes the addAndPrint() method. In the implementation of the Java virtual machine represented in this figure, each frame is allocated separately from a heap. To invoke the addTwoTypes() method, the addAndPrint() method first pushes an int one and double 88.88 onto its operand stack. It then invokes the addTwoTypes() method.



Figure 5-11. Allocating frames from a heap.

The instruction to invoke addTwoTypes() refers to a constant pool entry. The Java virtual machine looks up the entry and resolves it if necessary.

Note that the addAndPrint() method uses the constant pool to identify the addTwoTypes() method, even though it is part of the same class. Like references to fields and methods of other classes, references to the fields and methods of the same class are initially symbolic and must be resolved before they are used.

The resolved constant pool entry points to information in the method area about the addTwoTypes() method. The virtual machine uses this information to determine the sizes required by addTwoTypes() for the local variables and operand stack. In the class file generated by Sun's javac compiler from the JDK 1.1, addTwoTypes() requires three words in the local variables and four words in the operand stack. (As mentioned earlier, the size of the frame data portion is implementation dependent.) The virtual machine allocates enough memory for the addTwoTypes() frame from a heap. It then pops the double and int parameters (88.88 and one) from addAndPrint()'s operand stack and places them into addTwoType()'s local variable slots one and zero.

When addTwoTypes() returns, it first pushes the double return value (in this case, 89.88) onto its operand stack. The virtual machine uses the information in the frame data to locate the stack frame of the invoking method, addAndPrint(). It pushes the double return value onto addAndPrint()'s operand stack and frees the memory occupied by addTwoType()'s frame. It makes addAndPrint()'s frame current and continues executing the addAndPrint() method at the first instruction past the addTwoType() method invocation.

Figure 5-12 shows snapshots of the Java stack of a different virtual machine implementation executing the same methods. Instead of allocating each frame separately from a heap, this implementation allocates frames from a contiguous stack. This approach allows the implementation to overlap the frames of adjacent methods. The portion of the invoking method's operand stack that contains the parameters to the invoked method become the base of the invoked method's local variables. In this example, addAndPrint()'s entire operand stack becomes addTwoType()'s entire local variables section.



Figure 5-12. Allocating frames from a contiguous stack.

This approach saves memory space because the same memory is used by the calling method to store the parameters as is used by the invoked method to access the parameters. It saves time because the Java virtual machine doesn't have to spend time copying the parameter values from one frame to another.

Note that the operand stack of the current frame is always at the "top" of the Java stack. Although this may be easier to visualize in the contiguous memory implementation of Figure 5-12, it is true no matter how the Java stack is implemented. (As mentioned earlier, in all the graphical images of the stack shown in this book, the stack grows downwards. The "top" of the stack is always shown at the bottom of the picture.) Instructions that push values onto (or pop values off of) the operand stack always operate on the current frame. Thus, pushing a value onto the operand stack can be seen as pushing a value onto the top of the entire Java stack. In the remainder of this book, "pushing a value onto the stack" refers to pushing a value onto the operand stack of the current frame.

One other possible approach to implementing the Java stack is a hybrid of the two approaches shown in Figure 5-11 and Figure 5-12. A Java virtual machine implementation can allocate a chunk of contiguous memory from a heap when a thread starts. In this memory, the virtual machine can use the overlapping frames approach shown in Figure 5-12. If the stack outgrows the contiguous memory, the virtual machine can allocate another chunk of contiguous memory from the heap. It can use the separate frames approach shown in Figure 5-11 to connect the invoking method's frame sitting in the old chunk with the invoked method's frame sitting in the new chunk. Within the new chunk, it can once again use the contiguous memory approach.

Native Method Stacks

In addition to all the runtime data areas defined by the Java virtual machine specification and described previously, a running Java application may use other data areas created by or for native methods. When a thread invokes a native method, it enters a new world in which the structures and security restrictions of the Java virtual machine no longer hamper its freedom. A native method can likely access the runtime data areas of the virtual machine (it depends upon the native method interface), but can also do anything else it wants. It may use registers inside the native processor, allocate memory on any number of native heaps, or use any kind of stack.

Native methods are inherently implementation dependent. Implementation designers are free to decide what mechanisms they will use to enable a Java application running on their implementation to invoke native methods.

Any native method interface will use some kind of native method stack. When a thread invokes a Java method, the virtual machine creates a new frame and pushes it onto the Java stack. When a thread invokes a native method, however, that thread leaves the Java stack behind. Instead of pushing a new frame onto the thread's Java stack, the Java virtual machine will simply dynamically link to and directly invoke the native method. One way to think of it is that the Java virtual machine is dynamically extending itself with native code. It is as if the Java virtual machine implementation is just calling another (dynamically linked) method within itself, at the behest of the running Java program.

If an implementation's native method interface uses a C-linkage model, then the native method stacks are C stacks. When a C program invokes a C function, the stack operates in a certain way. The arguments to the function are pushed onto the stack in a certain order. The return value is passed back to the invoking function in a certain way. This would be the behavior of the of native method stacks in that implementation.

A native method interface will likely (once again, it is up to the designers to decide) be able to call back into the Java virtual machine and invoke a Java method. In this case, the thread leaves the native method stack and enters another Java stack.

Figure 5-13 shows a graphical depiction of a thread that invokes a native method that calls back into the virtual machine to invoke another Java method. This figure shows the full picture of what a thread can expect inside the Java virtual machine. A thread may spend its entire lifetime executing Java methods, working with frames on its Java stack. Or, it may jump back and forth between the Java stack and native method stacks.



Figure 5-13. The stack for a thread that invokes Java and native methods.

As depicted in Figure 5-13, a thread first invoked two Java methods, the second of which invoked a native method. This act caused the virtual machine to use a native method stack. In this figure, the native method stack is shown as a finite amount of contiguous memory space. Assume it is a C stack. The stack area used by each C-linkage function is shown in gray and bounded by a dashed line. The first C-linkage function, which was invoked as a native method, invoked another C-linkage function. The second C-linkage function invoked a Java method through the native method interface. This Java method invoked another Java method, which is the current method shown in the figure.

As with the other runtime memory areas, the memory they occupied by native method stacks need not be of a fixed size. It can expand and contract as needed by the running application. Implementations may allow users or programmers to specify an initial size for the method area, as well as a maximum or minimum size.

Execution Engine

At the core of any Java virtual machine implementation is its execution engine. In the Java virtual machine specification, the behavior of the execution engine is defined in terms of an instruction set. For each instruction, the specification describes in detail what an implementation should do when it encounters the instruction as it executes bytecodes, but says very little about how. As mentioned in previous chapters, implementation designers are free to decide how their implementations will execute bytecodes. Their implementations can interpret, just-in-time compile, execute natively in silicon, use a combination of these, or dream up some brand new technique.

Similar to the three senses of the term "Java virtual machine" described at the beginning of this chapter, the term "execution engine" can also be used in any of three senses: an abstract specification, a concrete implementation, or a runtime instance. The abstract specification defines the behavior of an execution engine in terms of the instruction set. Concrete implementations, which may use a variety of techniques, are either software, hardware, or a combination of both. A runtime instance of an execution engine is a thread.

Each thread of a running Java application is a distinct instance of the virtual machine's execution engine. From the beginning of its lifetime to the end, a thread is either executing bytecodes or native methods. A thread may execute bytecodes directly, by interpreting or executing natively in silicon, or indirectly, by just- in-time compiling and executing the resulting native code. A Java virtual machine implementation may use other threads invisible to the running application, such as a thread that performs garbage collection. Such threads need not be "instances" of the implementation's execution engine. All threads that belong to the running application, however, are execution engines in action.

The Instruction Set

A method's bytecode stream is a sequence of instructions for the Java virtual machine. Each instruction consists of a one-byte opcode followed by zero or more operands. The opcode indicates the operation to be performed. Operands supply extra information needed by the Java virtual machine to perform the operation specified by the opcode. The opcode itself indicates whether or not it is followed by operands, and the form the operands (if any) take. Many Java virtual machine instructions take no operands, and therefore consist only of an opcode. Depending upon the opcode, the virtual machine may refer to data stored in other areas in addition to (or instead of) operands that trail the opcode. When it executes an instruction, the virtual machine may use entries in the current constant pool, entries in the current frame's local variables, or values sitting on the top of the current frame's operand stack.

The abstract execution engine runs by executing bytecodes one instruction at a time. This process takes place for each thread (execution engine instance) of the application running in the Java virtual machine. An execution engine fetches an opcode and, if that opcode has operands, fetches the operands. It executes the action requested by the opcode and its operands, then fetches another opcode. Execution of bytecodes continues until a thread completes either by returning from its starting method or by not catching a thrown exception.

From time to time, the execution engine may encounter an instruction that requests a native method invocation. On such occasions, the execution engine will dutifully attempt to invoke that native method. When the native method returns (if it completes normally, not by throwing an exception), the execution engine will continue executing the next instruction in the bytecode stream.

One way to think of native methods, therefore, is as programmer-customized extensions to the Java virtual machine's instruction set. If an instruction requests an invocation of a native method, the execution engine invokes the native method. Running the native method is how the Java virtual machine executes the instruction. When the native method returns, the virtual machine moves on to the next instruction. If the native method completes abruptly (by throwing an exception), the virtual machine follows the same steps to handle the exception as it does when any instruction throws an exception.

Part of the job of executing an instruction is determining the next instruction to execute. An execution engine determines the next opcode to fetch in one of three ways. For many instructions, the next opcode to execute directly follows the current opcode and its operands, if any, in the bytecode stream. For some instructions, such as goto or return, the execution engine determines the next opcode as part of its execution of the current instruction. If an instruction throws an exception, the execution engine determines the next opcode to fetch by searching for an appropriate catch clause.

Several instructions can throw exceptions. The athrow instruction, for example, throws an exception explicitly. This instruction is the compiled form of the throw statement in Java source code. Every time the athrow instruction is executed, it will throw an exception. Other instructions throw exceptions only when certain conditions are encountered. For example, if the Java virtual machine discovers, to its chagrin, that the program is attempting to perform an integer divide by zero, it will throw an ArithmeticException. This can occur while executing any of four instructions--idiv, ldiv, irem, and lrem--which perform divisions or calculate remainders on ints or longs.

Each type of opcode in the Java virtual machine's instruction set has a mnemonic. In the typical assembly language style, streams of Java bytecodes can be represented by their mnemonics followed by (optional) operand values.

For an example of method's bytecode stream and mnemonics, consider the doMathForever() method of this class:

// On CD-ROM in file jvm/ex4/Act.java
class Act {

    public static void doMathForever() {
        int i = 0;
        for (;;) {
            i += 1;
            i *= 2;
        }
    }
}

The stream of bytecodes for doMathForever() can be disassembled into mnemonics as shown next. The Java virtual machine specification does not define any official syntax for representing the mnemonics of a method's bytecodes. The code shown next illustrates the manner in which streams of bytecode mnemonics will be represented in this book. The left hand column shows the offset in bytes from the beginning of the method's bytecodes to the start of each instruction. The center column shows the instruction and any operands. The right hand column contains comments, which are preceded with a double slash, just as in Java source code.

// Bytecode stream: 03 3b 84 00 01 1a 05 68 3b a7 ff f9
// Disassembly:
// Method void doMathForever()
// Left column: offset of instruction from beginning of method
// |   Center column: instruction mnemonic and any operands
// |   |                   Right column: comment
   0   iconst_0           // 03
   1   istore_0           // 3b
   2   iinc 0, 1          // 84 00 01
   5   iload_0            // 1a
   6   iconst_2           // 05
   7   imul               // 68
   8   istore_0           // 3b
   9   goto 2             // a7 ff f9

This way of representing mnemonics is very similar to the output of the javap program of Sun's Java 2 SDK. javap allows you to look at the bytecode mnemonics of the methods of any class file. Note that jump addresses are given as offsets from the beginning of the method. The goto instruction causes the virtual machine to jump to the instruction at offset two (an iinc). The actual operand in the stream is minus seven. To execute this instruction, the virtual machine adds the operand to the current contents of the pc register. The result is the address of the iinc instruction at offset two. To make the mnemonics easier to read, the operands for jump instructions are shown as if the addition has already taken place. Instead of saying "goto -7," the mnemonics say, "goto 2."

The central focus of the Java virtual machine's instruction set is the operand stack. Values are generally pushed onto the operand stack before they are used. Although the Java virtual machine has no registers for storing arbitrary values, each method has a set of local variables. The instruction set treats the local variables, in effect, as a set of registers that are referred to by indexes. Nevertheless, other than the iinc instruction, which increments a local variable directly, values stored in the local variables must be moved to the operand stack before being used.

For example, to divide one local variable by another, the virtual machine must push both onto the stack, perform the division, and then store the result back into the local variables. To move the value of an array element or object field into a local variable, the virtual machine must first push the value onto the stack, then store it into the local variable. To set an array element or object field to a value stored in a local variable, the virtual machine must follow the reverse procedure. First, it must push the value of the local variable onto the stack, then pop it off the stack and into the array element or object field on the heap.

Several goals--some conflicting--guided the design of the Java virtual machine's instruction set. These goals are basically the same as those described in Part I of this book as the motivation behind Java's entire architecture: platform independence, network mobility, and security.

The platform independence goal was a major influence in the design of the instruction set. The instruction set's stack-centered approach, described previously, was chosen over a register-centered approach to facilitate efficient implementation on architectures with few or irregular registers, such as the Intel 80X86. This feature of the instruction set--the stack-centered design--make it easier to implement the Java virtual machine on a wide variety of host architectures.

Another motivation for Java's stack-centered instruction set is that compilers usually use a stack-based architecture to pass an intermediate compiled form or the compiled program to a linker/optimizer. The Java class file, which is in many ways similar to the UNIX .o or Windows .obj file emitted by a C compiler, really represents an intermediate compiled form of a Java program. In the case of Java, the virtual machine serves as (dynamic) linker and may serve as optimizer. The stack-centered architecture of the Java virtual machine's instruction set facilitates the optimization that may be performed at run-time in conjunction with execution engines that perform just-in-time compiling or adaptive optimization.

As mentioned in Chapter 4, "Network Mobility," one major design consideration was class file compactness. Compactness is important because it facilitates speedy transmission of class files across networks. In the bytecodes stored in class files, all instructions--except two that deal with table jumping--are aligned on byte boundaries. The total number of opcodes is small enough so that opcodes occupy only one byte. This design strategy favors class file compactness possibly at the cost of some performance when the program runs. In some Java virtual machine implementations, especially those executing bytecodes in silicon, the single-byte opcode may preclude certain optimizations that could improve performance. Also, better performance may have been possible on some implementations if the bytecode streams were word-aligned instead of byte-aligned. (An implementation could always realign bytecode streams, or translate opcodes into a more efficient form as classes are loaded. Bytecodes are byte-aligned in the class file and in the specification of the abstract method area and execution engine. Concrete implementations can store the loaded bytecode streams any way they wish.)

Another goal that guided the design of the instruction set was the ability to do bytecode verification, especially all at once by a data flow analyzer. The verification capability is needed as part of Java's security framework. The ability to use a data flow analyzer on the bytecodes when they are loaded, rather than verifying each instruction as it is executed, facilitates execution speed. One way this design goal manifests itself in the instruction set is that most opcodes indicate the type they operate on.

For example, instead of simply having one instruction that pops a word from the operand stack and stores it in a local variable, the Java virtual machine's instruction set has two. One instruction, istore, pops and stores an int. The other instruction, fstore, pops and stores a float. Both of these instructions perform the exact same function when executed: they pop a word and store it. Distinguishing between popping and storing an int versus a float is important only to the verification process.

For many instructions, the virtual machine needs to know the types being operated on to know how to perform the operation. For example, the Java virtual machine supports two ways of adding two words together, yielding a one-word result. One addition treats the words as ints, the other as floats. The difference between these two instructions facilitates verification, but also tells the virtual machine whether it should perform integer or floating point arithmetic.

A few instructions operate on any type. The dup instruction, for example, duplicates the top word of a stack irrespective of its type. Some instructions, such as goto, don't operate on typed values. The majority of the instructions, however, operate on a specific type. The mnemonics for most of these "typed" instructions indicate their type by a single character prefix that starts their mnemonic. Table 5-2 shows the prefixes for the various types. A few instructions, such as arraylength or instanceof, don't include a prefix because their type is obvious. The arraylength opcode requires an array reference. The instanceof opcode requires an object reference.

Type Code Example Description
byte b baload load byte from array
short s saload load short from array
int i iaload load int from array
long l laload load long from array
char c caload load char from array
float f faload load float from array
double d daload load double from array
reference a aaload load reference from array

Table 5-2. Type prefixes of bytecode mnemonics

Values on the operand stack must be used in a manner appropriate to their type. It is illegal, for example, to push four ints, then add them as if they were two longs. It is illegal to push a float value onto the operand stack from the local variables, then store it as an int in an array on the heap. It is illegal to push a double value from an object field on the heap, then store the topmost of its two words into the local variables as an value of type reference. The strict type rules that are enforced by Java compilers must also be enforced by Java virtual machine implementations.

Implementations must also observe rules when executing instructions that perform generic stack operations independent of type. As mentioned previously, the dup instruction pushes a copy of the top word of the stack, irrespective of type. This instruction can be used on any value that occupies one word: an int, float, reference, or returnAddress. It is illegal, however, to use dup when the top of the stack contains either a long or double, the data types that occupy two consecutive operand stack locations. A long or double sitting on the top of the operand stack can be duplicated in their entirety by the dup2 instruction, which pushes a copy of the top two words onto the operand stack. The generic instructions cannot be used to split up dual-word values.

To keep the instruction set small enough to enable each opcode to be represented by a single byte, not all operations are supported on all types. Most operations are not supported for types byte, short, and char. These types are converted to int when moved from the heap or method area to the stack frame. They are operated on as ints, then converted back to byte, short, or char before being stored back into the heap or method area.

Table 5-3 shows the computation types that correspond to each storage type in the Java virtual machine. As used here, a storage type is the manner in which values of the type are represented on the heap. The storage type corresponds to the type of the variable in Java source code. A computation type is the manner in which the type is represented on the Java stack frame.

Storage Type Minimum Bits in Heap
or Method Area
Computation Type Words in the
Java Stack Frame
byte
8
int
1
short
16
int
1
int
32
int
1
long
64
long
2
char
16
int
1
float
32
float
1
double
64
double
2
reference
32
reference
1

Table 5-3. Storage and computation types inside the Java virtual machine

Implementations of the Java virtual machine must in some way ensure that values are operated on by instructions appropriate to their type. They can verify bytecodes up front as part of the class verification process, on the fly as the program executes, or some combination of both. Bytecode verification is described in more detail in Chapter 7, "The Lifetime of a Type." The entire instruction set is covered in detail in Chapters 10 through 20

Execution Techniques

Various execution techniques that may be used by an implementation--interpreting, just-in-time compiling, adaptive optimization, native execution in silicon--were described in Chapter 1, "Introduction to Java's Architecture." The main point to remember about execution techniques is that an implementation can use any technique to execute bytecodes so long as it adheres to the semantics of the Java virtual machine instruction set.

One of the most interesting -- and speedy -- execution techniques is adaptive optimization. The adaptive optimization technique, which is used by several existing Java virtual machine implementations, including Sun's Hotspot virtual machine, borrows from techniques used by earlier virtual machine implementations. The original JVMs interpreted bytecodes one at a time. Second-generation JVMs added a JIT compiler, which compiles each method to native code upon first execution, then executes the native code. Thereafter, whenever the method is called, the native code is executed. Adaptive optimizers, taking advantage of information available only at run-time, attempt to combine bytecode interpretation and compilation to native in the way that will yield optimum performance.

An adaptive optimizing virtual machine begins by interpreting all code, but it monitors the execution of that code. Most programs spend 80 to 90 percent of their time executing 10 to 20 percent of the code. By monitoring the program execution, the virtual machine can figure out which methods represent the program's "hot spot" -- the 10 to 20 percent of the code that is executed 80 to 90 percent of the time.

When the adaptive optimizing virtual machine decides that a particular method is in the hot spot, it fires off a background thread that compiles those bytecodes to native and heavily optimizes the native code. Meanwhile, the program can still execute that method by interpreting its bytecodes. Because the program isn't held up and because the virtual machine is only compiling and optimizing the "hot spot" (perhaps 10 to 20 percent of the code), the virtual machine has more time than a traditional JIT to perform optimizations.

The adaptive optimization approach yields a program in which the code that is executed 80 to 90 percent of the time is native code as heavily optimized as statically compiled C++, with a memory footprint not much bigger than a fully interpreted Java program. In other words, fast. An adaptive optimizing virtual machine can keep the old bytecodes around in case a method moves out of the hot spot. (The hot spot may move somewhat as the program executes.) If a method moves out of the hot spot, the virtual machine can discard the compiled code and revert back to interpreting that method's bytecodes.

As you may have noticed, an adaptive optimizer's approach to making Java programs run fast is similar to the approach programmers should take to improve a program's performance. An adaptive optimizing virtual machine, unlike a regular JIT compiling virtual machine, doesn't do "premature optimization." The adaptive optimizing virtual machine begins by interpreting bytecodes. As the program runs, the virtual machine "profiles" the program to find the program's "hot spot," that 10 to 20 percent of the code that gets executed 80 to 90 percent of the time. And like a good programmer, the adaptive optimizing virtual machine just focuses its optimization efforts on that time-critical code.

But there is a bit more to the adaptive optimization story. Adaptive optimizers can be tuned for the run- time characteristics of Java programs -- in particular, of "well- designed" Java programs. According to David Griswold, Hotspot manager at JavaSoft, "Java is a lot more object-oriented than C++. You can measure that; you can look at the rates of method invocations, dynamic dispatches, and such things. And the rates [for Java] are much higher than they are in C++." Now this high rate of method invocations and dynamic dispatches is especially true in a well-designed Java program, because one aspect of a well-designed Java program is highly factored, fine-grained design -- in other words, lots of compact, cohesive methods and compact, cohesive objects.

This run-time characteristic of Java programs, the high frequency of method invocations and dynamic dispatches, affects performance in two ways. First, there is an overhead associated with each dynamic dispatch. Second, and more significantly, method invocations reduce the effectiveness of compiler optimization.

Method invocations reduce the effectiveness of optimizers because optimizers don't perform well across method invocation boundaries. As a result, optimizers end up focusing on the code between method invocations. And the greater the method invocation frequency, the less code the optimizer has to work with between method invocations, and the less effective the optimization becomes.

The standard solution to this problem is inlining -- the copying of an invoked method's body directly into the body of the invoking method. Inlining eliminates method calls and gives the optimizer more code to work with. It makes possible more effective optimization at the cost of increasing the run- time memory footprint of the program.

The trouble is that inlining is harder with object-oriented languages, such as Java and C++, than with non-object-oriented languages, such as C, because object-oriented languages use dynamic dispatching. And the problem is worse in Java than in C++, because Java has a greater call frequency and a greater percentage of dynamic dispatches than C++.

A regular optimizing static compiler for a C program can inline straightforwardly because there is one function implementation for each function call. The trouble with doing inlining with object- oriented languages is that dynamic method dispatch means there may be multiple function (or method) implementation for any given function call. In other words, the JVM may have many different implementations of a method to choose from at run time, based on the class of the object on which the method is being invoked.

One solution to the problem of inlining a dynamically dispatched method call is to just inline all of the method implementations that may get selected at run-time. The trouble with this solution is that in cases where there are a lot of method implementations, the size of the optimized code can grow very large.

One advantage adaptive optimization has over static compilation is that, because it is happening at runtime, it can use information not available to a static compiler. For example, even though there may be 30 possible implementations that may get called for a particular method invocation, at run-time perhaps only two of them are ever called. The adaptive optimization approach enables only those two to be inlined, thereby minimizing the size of the optimized code.

Threads

The Java virtual machine specification defines a threading model that aims to facilitate implementation on a wide variety of architectures. One goal of the Java threading model is to enable implementation designers, where possible and appropriate, to use native threads. Alternatively, designers can implement a thread mechanism as part of their virtual machine implementation. One advantage to using native threads on a multi-processor host is that different threads of a Java application could run simultaneously on different processors.

One tradeoff of Java's threading model is that the specification of priorities is lowest-common- denominator. A Java thread can run at any one of ten priorities. Priority one is the lowest, and priority ten is the highest. If designers use native threads, they can map the ten Java priorities onto the native priorities however seems most appropriate. The Java virtual machine specification defines the behavior of threads at different priorities only by saying that all threads at the highest priority will get some CPU time. Threads at lower priorities are guaranteed to get CPU time only when all higher priority threads are blocked. Lower priority threads may get some CPU time when higher priority threads aren't blocked, but there are no guarantees.

The specification doesn't assume time-slicing between threads of different priorities, because not all architectures time-slice. (As used here, time-slicing means that all threads at all priorities will be guaranteed some CPU time, even when no threads are blocked.) Even among those architectures that do time-slice, the algorithms used to allot time slots to threads at various priorities can differ greatly.

As mentioned in Chapter 2, "Platform Independence," you must not rely on time-slicing for program correctness. You should use thread priorities only to give the Java virtual machine hints at what it should spend more time on. To coordinate the activities of multiple threads, you should use synchronization.

The thread implementation of any Java virtual machine must support two aspects of synchronization: object locking and thread wait and notify. Object locking helps keep threads from interfering with one another while working independently on shared data. Thread wait and notify helps threads to cooperate with one another while working together toward some common goal. Running applications access the Java virtual machine's locking capabilities via the instruction set, and its wait and notify capabilities via the wait(), notify(), and notifyAll() methods of class Object. For more details, see Chapter 20, "Thread Synchronization."

In the Java virtual machine Specification, the behavior of Java threads is defined in terms of variables, a main memory, and working memories. Each Java virtual machine instance has a main memory, which contains all the program's variables: instance variables of objects, components of arrays, and class variables. Each thread has a working memory, in which the thread stores "working copies" of variables it uses or assigns. Local variables and parameters, because they are private to individual threads, can be logically seen as part of either the working memory or main memory.

The Java virtual machine specification defines many rules that govern the low-level interactions of threads with main memory. For example, one rule states that all operations on primitive types, except in some cases longs and doubles, are atomic. For example, if two threads compete to write two different values to an int variable, even in the absence of synchronization, the variable will end up with one value or the other. The variable will not contain a corrupted value. In other words, one thread will win the competition and write its value to the variable first. The losing thread need not sulk, however, because it will write its value the variable second, overwriting the "winning" thread's value.

The exception to this rule is any long or double variable that is not declared volatile. Rather than being treated as a single atomic 64-bit value, such variables may be treated by some implementations as two atomic 32-bit values. Storing a non-volatile long to memory, for example, could involve two 32-bit write operations. This non- atomic treatment of longs and doubles means that two threads competing to write two different values to a long or double variable can legally yield a corrupted result.

Although implementation designers are not required to treat operations involving non-volatile longs and doubles atomically, the Java virtual machine specification encourages them to do so anyway. This non-atomic treatment of longs and doubles is an exception to the general rule that operations on primitive types are atomic. This exception is intended to facilitate efficient implementation of the threading model on processors that don't provide efficient ways to transfer 64-bit values to and from memory. In the future, this exception may be eliminated. For the time being, however, Java programmers must be sure to synchronize access to shared longs and doubles.

Fundamentally, the rules governing low-level thread behavior specify when a thread may and when it must:

  1. copy values of variables from the main memory to its working memory, and
  2. write values from its working memory back into the main memory.

For certain conditions, the rules specify a precise and predictable order of memory reads and writes. For other conditions, however, the rules do not specify any order. The rules are designed to enable Java programmers to build multi-threaded programs that exhibit predictable behavior, while giving implementation designers some flexibility. This flexibility enables designers of Java virtual machine implementations to take advantage of standard hardware and software techniques that can improve the performance of multi-threaded applications.

The fundamental high-level implication of all the low-level rules that govern the behavior of threads is this: If access to certain variables isn't synchronized, threads are allowed update those variables in main memory in any order. Without synchronization, your multi-threaded applications may exhibit surprising behavior on some Java virtual machine implementations. With proper use of synchronization, however, you can create multi-threaded Java applications that behave in a predictable way on any implementation of the Java virtual machine.

Native Method Interface

Java virtual machine implementations aren't required to support any particular native method interface. Some implementations may support no native method interfaces at all. Others may support several, each geared towards a different purpose.

Sun's Java Native Interface, or JNI, is geared towards portability. JNI is designed so it can be supported by any implementation of the Java virtual machine, no matter what garbage collection technique or object representation the implementation uses. This in turn enables developers to link the same (JNI compatible) native method binaries to any JNI-supporting virtual machine implementation on a particular host platform.

Implementation designers can choose to create proprietary native method interfaces in addition to, or instead of, JNI. To achieve its portability, the JNI uses a lot of indirection through pointers to pointers and pointers to functions. To obtain the ultimate in performance, designers of an implementation may decide to offer their own low-level native method interface that is tied closely to the structure of their particular implementation. Designers could also decide to offer a higher-level native method interface than JNI, such as one that brings Java objects into a component software model.

To do useful work, a native method must be able to interact to some degree with the internal state of the Java virtual machine instance. For example, a native method interface may allow native methods to do some or all of the following:

  • Pass and return data
  • Access instance variables or invoke methods in objects on the garbage-collected heap
  • Access class variables or invoke class methods
  • Accessing arrays
  • Lock an object on the heap for exclusive use by the current thread
  • Create new objects on the garbage-collected heap
  • Load new classes
  • Throw new exceptions
  • Catch exceptions thrown by Java methods that the native method invoked
  • Catch asynchronous exceptions thrown by the virtual machine
  • Indicate to the garbage collector that it no longer needs to use a particular object

Designing a native method interface that offers these services can be complicated. The design needs to ensure that the garbage collector doesn't free any objects that are being used by native methods. If an implementation's garbage collector moves objects to keep heap fragmentation at a minimum, the native method interface design must make sure that either:

  1. an object can be moved after its reference has been passed to a native method, or
  2. any objects whose references have been passed to a native method are pinned until the native method returns or otherwise indicates it is done with the objects As you can see, native method interfaces are very intertwined with the inner workings of a Java virtual machine.

The Real Machine

As mentioned at the beginning of this chapter, all the subsystems, runtime data areas, and internal behaviors defined by the Java virtual machine specification are abstract. Designers aren't required to organize their implementations around "real" components that map closely to the abstract components of the specification. The abstract internal components and behaviors are merely a vocabulary with which the specification defines the required external behavior of any Java virtual machine implementation.

In other words, an implementation can be anything on the inside, so long as it behaves like a Java virtual machine on the outside. Implementations must be able to recognize Java class files and must adhere to the semantics of the Java code the class files contain. But otherwise, anything goes. How bytecodes are executed, how the runtime data areas are organized, how garbage collection is accomplished, how threads are implemented, how the bootstrap class loader finds classes, what native method interfaces are supported-- these are some of the many decisions left to implementation designers.

The flexibility of the specification gives designers the freedom to tailor their implementations to fit their circumstances. In some implementations, minimizing usage of resources may be critical. In other implementations, where resources are plentiful, maximizing performance may be the one and only goal.

By clearly marking the line between the external behavior and the internal implementation of a Java virtual machine, the specification preserves compatibility among all implementations while promoting innovation. Designers are encouraged to apply their talents and creativity towards building ever-better Java virtual machines.

Eternal Math: A Simulation

The CD-ROM contains several simulation applets that serve as interactive illustrations for the material presented in this book. The applet shown in Figure 5-14 simulates a Java virtual machine executing a few bytecodes. You can run this applet by loading applets/EternalMath.html from the CD-ROM into any Java enabled web browser or applet viewer that supports JDK 1.0.

The instructions in the simulation represent the body of the doMathForever() method of class Act, shown previously in the "Instruction Set" section of this chapter. This simulation shows the local variables and operand stack of the current frame, the pc register, and the bytecodes in the method area. It also shows an optop register, which you can think of as part of the frame data of this particular implementation of the Java virtual machine. The optop register always points to one word beyond the top of the operand stack.

The applet has four buttons: Step, Reset, Run, and Stop. Each time you press the Step button, the Java virtual machine simulator will execute the instruction pointed to by the pc register. Initially, the pc register points to an iconst_0 instruction. The first time you press the Step button, therefore, the virtual machine will execute iconst_0. It will push a zero onto the stack and set the pc register to point to the next instruction to execute. Subsequent presses of the Step button will execute subsequent instructions and the pc register will lead the way. If you press the Run button, the simulation will continue with no further coaxing on your part until you press the Stop button. To start the simulation over, press the Reset button.

The value of each register (pc and optop) is shown two ways. The contents of each register, an integer offset from the beginning of either the method's bytecodes or the operand stack, is shown in an edit box. Also, a small arrow (either "pc>" or "optop>") indicates the location contained in the register.

In the simulation the operand stack is shown growing down the panel (up in memory offsets) as words are pushed onto it. The top of the stack recedes back up the panel as words are popped from it.

The doMathForever() method has only one local variable, i, which sits at array position zero. The first two instructions, iconst_0 and istore_0 initialize the local variable to zero. The next instruction, iinc, increments i by one. This instruction implements the i += 1 statement from doMathForever(). The next instruction, iload_0, pushes the value of the local variable onto the operand stack. iconst_2 pushes an int 2 onto the operand stack. imul pops the top two ints from the operand stack, multiplies them, and pushes the result. The istore_0 instruction pops the result of the multiply and puts it into the local variable. The previous four instructions implement the i *= 2 statement from doMathForever(). The last instruction, goto, sends the program counter back to the iinc instruction. The goto implements the for (;;) loop of doMathForever().

With enough patience and clicks of the Step button (or a long enough run of the Run button), you can get an arithmetic overflow. When the Java virtual machine encounters such a condition, it just truncates, as is shown by this simulation. It does not throw any exceptions.

For each step of the simulation, a panel at the bottom of the applet contains an explanation of what the next instruction will do. Happy clicking.



Figure 5-14. The Eternal Math applet.

On the CD-ROM

The CD-ROM contains the source code examples from this chapter in the jvm directory. The Eternal Math applet is contained in a web page on the CD-ROM in file applets/EternalMath.html. The source code for this applet is found alongside its class files, in the applets/JVMSimulators and applets/JVMSimulators/COM/artima/jvmsim directories.

The Resources Page

For links to more information about the Java virtual machine, visit the resources page: http://www.artima.com/insidejvm/resources/


2007/07/24 11:10 2007/07/24 11:10
이 글에는 트랙백을 보낼 수 없습니다
20세기 말을 되돌아보며 --

정확히 1997년 Object Management Group (OMG)이 Unified Modeling Language (UML)을 발표했다. UML의 목표 중 하나는 개발 커뮤니티에 안정적이고, 일반적인 디자인 언어를 제공하는 것이었다. UML은 IT 전문가들이 수년 동안 바라던 통합된 표준 모델링 표기법을 탄생시켰다. UML을 사용하여 IT 전문가들은 시스템 구조와 디자인 계획을 읽고 분산시킬 수 있다. 건축가들이 빌딩의 청사진을 사용하는 것처럼 말이다.

이제 21세기가 되었고 UML은 전문성을 확립하게 되었다. 내가 보고 있는 이력서의 75 퍼센트 정도가 UML을 알고 있다고 쓰여있다. 하지만 면접을 통해 이야기를 해보면 이들이 진정으로 UML을 알지 못하고 있다는 것이 명확해진다. 일반적으로 당시 이슈가 되는 키워드 로서 알고 있거나 표면적인 면만 알고 있는 경우가 대부분이었다. 이것이 바로 내가 이 글을 쓴 이유이다. 이 글을 다 읽었다고 해서 이력서에 UML을 충분히 알고 있다고 쓸 수는 없겠지만, 이 언어를 보다 심도 깊게 연구할 출발선에는 설 정도는 된 것이다.

배경 지식

UML은 컴퓨터 애플리케이션을 모델링 할 수 있는 통합 언어이다. 주요 작성자들은 Jim Rumbaugh, Ivar Jacobson, Grady Booch이고 이들은 원래 그들만의 꽤 괜찮은 방식(OMT, OOSE, Booch)을 갖고 있었다. 결국 이들은 힘을 합쳤고 개방형 표준을 만들었다. (어디서 많이 들어본 소리인가? J2EE, SOAP, Linux도 비슷한 현상이다.) UML이 표준 모델링 언어가 된 한 가지 이유는 이것이 프로그래밍 언어에 독립적이라는데 있다. (IBM Rational의 UML 모델링 툴은 .NET 뿐만 아니라 J2EE에서도 사용된다.) 또한 UML 표기법 세트는 언어이지 방법론이 아니다. 언어인 것이 중요한 이유는 방법론과는 반대로 언어는 기업이 비즈니스를 수행하는 방식에 잘 맞는다.

UML은 방법론이 아니기 때문에 (IBM Rational Unified Process® lingo의 "객체(artifacts)" 같은) 어떤 형식적인 작업 생성물들이 필요 없다. 하지만 정해진 방법론 안에서 쓰이면, 애플리케이션을 개발할 때 애플리케이션을 쉽게 이해할 수 있도록 도와주는 여러 가지 유형의 다이어그램을 제공한다. 이 다이어그램은 현재 사용하고 있는 것의 언어와 원리를 잘 소개하고 있다. 사용중인 방법론에서 생긴 작업 생산품들에 표준 UML 다이어그램을 배치하여 UML에 능숙한 사람들이 프로젝트에 쉽게 참여하여 생산성을 높일 수 있도록 한다. 가장 유용한 표준 UML 다이어그램은 사용 케이스 다이어그램, 클래스 다이어그램, 시퀀스 다이어그램, 스테이트 차트 다이어그램, 액티비티 다이어그램, 컴포넌트 다이어그램, 전개 다이어그램 등이 있다.

각 유형의 다이어그램을 자세히 설명하지는 않겠다. 대신, 각 다이어그램에 대한 일반적인 개념을 소개하고 자세한 것은 나중에 다루도록 하겠다.




위로


사용 케이스 다이어그램

사용 케이스는 시스템에서 제공한 기능 단위를 설명한다. 사용 케이스 다이어그램의 주요 목적은, 다른 사용 케이스들 간 관계 뿐만 아니라 주요 프로세스에 대한 "액터(actors)" (시스템과 인터랙팅하는 사람)들과의 관계를 포함하여, 개발 팀들이 시스템의 기능적 요구 사항들을 시각화 하는 데 있다. 사용 케이스 다이어그램은 사용 케이스 그룹들을 보여준다. 완전한 시스템에 대한 모든 사용 케이스이거나 관련된 기능을 가진 특정 사용 케이스 그룹(예를 들어, 보안 관리에 관련된 사용 케이스 그룹)의 사용 케이스일 수도 있다. 사용 케이스 다이어그램에 대한 사용 케이스를 보여주려면 다이어그램 중간에 타원을 그려서, 타원의 중앙 또는 아래에 사용 케이스 이름을 적어놓는다. 사용 케이스 다이어그램에 액터(시스템 사용자)를 그리려면 다이어그램의 왼쪽이나 오른쪽에 사람 모양을 그려 넣는다. (얼마나 예쁘게 그리는가는 여러분에게 달려있다.) 액터와 사용 케이스들간 관계는 그림 1에 나타나있다.

그림 1: 사용 케이스 다이어그램

사용 케이스 다이어그램은 시스템의 고급 기능과 시스템의 범위를 설명하는데 사용된다. 그림 1의 사용 케이스 다이어그램을 통해, 시스템이 제공하는 기능을 쉽게 표현할 수 있다. 이러한 시스템에서는 밴드 매니저가 밴드가 발매한 CD에 대한 판매 통계 리포트와 Billboard 200 보고서를 볼 수 있다. 또한 레코드 매니저는 특정 CD에 대한 판매 통계 보고서와 Billboard 200 보고서를 볼 수 있다. 이 다이어그램에서는 Billboard Reporting Service라고 하는 외부 시스템에서 우리 시스템이 Billboard 리포트를 전달하고 있다는 것도 볼 수 있다.

또한, 이 다이어그램에 사용 케이스가 없다는 것은 시스템이 수행하지 않은 일을 보여주고 있는 것이다. 예를 들어, 이 다이어그램은 밴드 매니저가 Billboard 200의 다른 앨범들에 수록된 노래를 듣는 방식은 나와있지 않다. Billboard 200에서 Listen to Songs 라는 사용 케이스에 대한 어떤 레퍼런스도 볼 수 없다. 이것은 중요한 포인트이다. 그와 같은 다이어그램에 제공된 명확하고 간단한 사용 케이스 설명을 통해 프로젝트 스폰서는 시스템에 필요한 기능이 존재하는지 여부를 쉽게 볼 수 있는 것이다.




위로


클래스 다이어그램

클래스 다이어그램은 다른 엔터티들(사람, 제품, 데이터)이 서로 어떻게 관계를 맺고 있는지를 보여준다. 다시 말해서, 이것은 시스템의 정적 구조라고 할 수 있다. 클래스 다이어그램은 록밴드, 씨디, 라디오 연주를 논리적 클래스로 나타내는데 사용될 수 있다. 또는 대출, 주택 저당 대출, 자동차 대출, 이자율을 나타내는데도 쓰일 수 있겠다. 클래스 다이어그램은 주로 프로그래머들이 다루는 구현 클래스들을 보여주는데 쓰인다. 구현 클래스 다이어그램은 논리적 클래스 다이어그램과 같은 클래스를 보여준다. 하지만 이 구현 클래스 다이어그램은 같은 애트리뷰트로는 그릴 수 없다. Vectors와 HashMaps 같은 것에 대한 레퍼런스를 갖고 있기 때문이다.

그림 2에서는 세 개의 섹션으로 클래스 다이어그램을 설명하고 있다. 위 섹션은 클래스의 이름을, 중간 섹션은 클래스의 애트리뷰트를, 가장 아래 섹션은 클래스의 연산(“그림 2에서는 세 개의 섹션으로 클래스 다이어그램을 설명하고 있다. 위 섹션은 클래스의 이름을, 중간 섹션은 클래스의 애트리뷰트를, 가장 아래 섹션은 클래스의 연산 ("메소드")을 보여주고 있다.

그림 2: 클래스 다이어그램의 클래스 객체

내 경험으로는 거의 모든 개발자들은 이 다이어그램이 무엇을 하고 있는지를 안다. 하지만 대부분의 프로그래머들은 관계도를 잘못 그리고 있다. 그림 3과 같은 클래스 다이어그램의 경우 상속 관계주 1를 그릴 때에는 화살표 방향을 위로 향하게 하여 수퍼 클래스를 지시하게 하면서 화살표 모양은 완벽한 삼각형이 되도록 해야 한다. 상관 관계는 두 클래스들이 서로를 인식하고 있다면 일직선이 되어야 하고, 클래스의 한 편만 알고 있는 관계라면 화살표 표시가 되어있는 선을 그어야 한다.

그림 3: 그림 2의 클래스 객체가 포함된 클래스 다이어그램

그림 3에서, 상속 관계와 두 개의 상관 관계를 보았다. CDSalesReport 클래스는 Report 클래스에서 상속을 받고, CDSalesReport는 한 개의 CD와 관련이 되어 있지만, CD 클래스는 CDSalesReport에 대해 아무것도 모르고 있다. CD와 Band 클래스는 서로에 대해 알고 있고, 두 클래스는 서로 연관되어 있다.

클래스 다이어그램에는 이 보다 더 많은 개념들을 적용할 수 있다. 나중에 설명하도록 하겠다.




위로


시퀀스 다이어그램

시퀀스 다이어그램은 특정 사용 케이스에 대한 상세한 흐름이나 심지어는 특정 사용 케이스의 일부분 까지도 보여준다. 대부분이 설명을 포함하고 있다. 시퀀스에서 다른 객체들 간의 호출관계를 보여주고 있고, 다른 객체들로의 다른 호출까지 상세하게 보여줄 수 있다.

시퀀스 다이어그램은 2차원으로 그려진다. 수직 차원은 발생 시간 순서로 메시지/호출 시퀀스를 보여주고 있다. 수평 차원은 메시지가 전송되는 객체 인스턴스를 나타내고 있다.

시퀀스 다이어그램은 그리기가 매우 간단하다. 다이어그램의 상단에 각 클래스 인스턴스를 박스 안에 놓아 클래스 인스턴스(객체)를 구분한다. (그림 4) 박스 안에 클래스 인스턴스 이름과 클래스 이름을 스페이스/콜론/스페이스 " : "로 분리시킨다. (예, myReportGenerator : ReportGenerator) 클래스 인스턴스가 메시지를 또 다른 클래스 인스턴스로 보내면 클래스 인스턴스를 받는 곳을 가리키는 화살표를 긋는다. 그 라인 위에 메시지/메소드 이름을 적는다. 중요한 메시지의 경우는 원래의 클래스 인스턴스를 다시 향하도록 점선 화살표를 그릴 수 있다. 점선 위에 리턴 값을 라벨링한다. 개인적으로는 리턴 값을 포함하곤 하는데 상세한 부분을 읽기 쉽기 때문이다.

시퀀스 다이어그램을 읽기는 매우 간단하다. 시퀀스를 시작하는 "드라이버(driver)" 클래스 인스턴스가 있는 왼쪽 상단 코너부터 시작한다. 그런 다음, 다이어그램 아래쪽을 각 메시지를 따라간다. 그림 4의 시퀀스 다이어그램 예제에서 전송 메시지에 대한 리턴 메시지는 선택적인 것임을 기억하라.

그림 4: 시퀀스 다이어그램

그림 4의 시퀀스 다이어그램을 읽다 보면 CD Sales Report가 어떻게 만들어지는지를 알 수 있다. aServlet 객체가 우리의 드라이버 예제이다. aServlet은 메시지를 gen이라고 하는 ReportGenerator 클래스 인스턴스로 보낸다. 이 메시지는 generateCDSalesReport 라는 라벨링이 붙는다. ReportGenerator 객체가 이 메시지 핸들러를 구현한다는 의미이다. 자세히 보면, generateCDSalesReport 메시지 라벨은 괄호 안에 cdId가 있다. gen 인스턴스가 generateCDSalesReport 메시지를 받으면 CDSalesReport로 연속 호출을 하고 aCDReport 라고 하는 CDSalesReport의 실제 인스턴스가 리턴 된다. gen 인스턴스는 리턴된 aCDReport 인스턴스에 호출하면서 여기에 각 메시지 호출에 대한 매개변수를 전달한다. 시퀀스의 끝에서 gen 인스턴스는 콜러였던 aServlet에 aCDReport를 리턴한다.

그림 4의 시퀀스 다이어그램은 전형적인 시퀀스 다이어그램을 상세히 설명한 것이다. 하지만 충분히 이해할 수 있을 것이다. 또한 초보 개발자들에게는 각 레벨 마다 시퀀스를 끊어서 이해하는 것도 좋은 방법이다.




위로


스테이트 차트 다이어그램

스테이트 차트 다이어그램은 클래스가 개입된 다양한 상태(state)를 모델링 하고 그 클래스가 상태간 어떻게 이동하는지를 모델링 한다. 모든 클래스는 상태를 갖고 있지만 각각의 클래스가 스테이트 차트 다이어그램을 가질 수 없다. "중요한" 상태, 말하자면 시스템 작동 중 세 개 이상의 잠재적 상태가 있는 클래스일 경우만 모델링 되어야 한다.

그림 5에서 보듯, 스테이트챠트 다이어그램에는 다섯 개의 기본 엘리먼트들이 사용된다. 시작점(짙은 원), 스테이트 간 이동(화살표), 스테이트(모서리가 둥근 직사각형), 결정 포인트(속이 비어있는 원), 한 개 이상의 종료점(원 내부에 짙은 원이 그려져 있음)이 바로 그것이다. 스테이트챠트 다이어그램을 그리려면 시작점과 클래스의 초기 상태를 향하는 화살표로 시작한다. 다이어그램 어디에나 이 스테이트를 그릴 수 있고 스테이트 이동 라인을 사용하여 연결한다.

그림 5:시스템 작동 중 클래스가 실행되는 다양한 상태를 보여주는 스테이트 차트 다이어그램

그림 5의 스테이트 차트 다이어그램은 중요한 정보를 보여주고 있다. 예를 들어, 대출 프로세스가 Loan Application 상태에서 출발한다고 말할 수 있다. 결과에 따라 사전 승인 프로세스가 완료되면 Loan Pre-approved 상태나 Loan Rejected 상태로 옮겨간다. 이동하는 동안 내린 결정은 결정 포인트로 보여진다. 이동 라인 상의 비어있는 원이 바로 그것이다. 이 예제를 보면 Loan Closing 상태를 거치지 않고는 대출이 Loan Pre-Approved 상태에서 Loan in Maintenance 상태로 갈 수 없음을 알 수 있다. 또한, 모든 대출이 Loan Rejected 상태 또는 Loan in Maintenance 상태에서 끝난다는 것도 알 수 있다.




위로


액티비티 다이어그램

액티비티 다이어그램은 액티비티를 처리하는 동안 두 개 이상의 클래스 객체들 간 제어 흐름을 보여준다. 액티비티 다이어그램은 비즈니스 단위 레벨에서 상위 레벨의 비즈니스 프로세스를 모델링 하거나 저수준 내부 클래스 액션을 모델링 하는데 사용된다. 내가 경험한 바로는 액티비티 다이어그램은 기업이 현재 어떻게 비즈니스를 수행하는지, 또는 어떤 것이 비즈니스에 어떤 작용을 하는지 등의 고차원 프로세스를 모델링 할 때 가장 적합하다. 액티비티 다이어그램은 언뜻 보기에 시퀀스 다이어그램 보다는 덜 기술적이기 때문에 비즈니스 마인드를 가진 사람들이 빠르게 이해할 수 있다.

액티비티 다이어그램의 표기법은 스테이트 차트 다이어그램과 비슷하다. 스테이트 차트 다이어그램과 마찬가지로 액티비티 다이어그램은 초기 액티비티에 연결된 실선으로 된 원에서 시작한다. 이 액티비티는 모서리가 둥근 직사각형을 그려 그 안에 액티비티 이름을 적어 넣으면서 모델링 된다. 액티비티들은 이동 라인을 통해 다른 액티비티들에 연결되거나 결정 포인트의 조건에 제약을 받는 다른 액티비티들에 연결하는 결정 포인트로 연결될 수 있다. 모델링 된 프로세스를 종료하는 액티비티는 (스테이트 차트 다이어그램에서처럼) 종료 포인트에 연결된다. 이 액티비티들은 수영 레인으로 그룹핑 될 수 있다. 이것은 실제로 액티비티를 수행하는 객체를 나타내는데 사용된다. (그림 6)

그림 6: 두 개의 객체(밴드 매니저와 리포팅 툴)에 의한 액티비티 제어를 나타내는 두 개의 수영 레인으로 되어있다.

그림 5의 스테이트 차트 다이어그램은 중요한 정보를 보여주고 있다. 예를 들어, 대출 프로세스가 Loan Application 상태에서 출발한다고 말할 수 있다. 결과에 따라 사전 승인 프로세스가 완료되면 Loan Pre-approved 상태나 Loan Rejected 상태로 옮겨간다. 이동하는 동안 내린 결정은 결정 포인트로 보여진다. 이동 라인 상의 비어있는 원이 바로 그것이다. 이 예제를 보면 Loan Closing 상태를 거치지 않고는 대출이 Loan Pre-Approved 상태에서 Loan in Maintenance 상태로 갈 수 없음을 알 수 있다. 또한, 모든 대출이 Loan Rejected 상태 또는 Loan in Maintenance 상태에서 끝난다는 것도 알 수 있다.

액티비티 다이어그램 예제는 두 개의 객체(밴드 매니저와 리포팅 툴)에 의한 액티비티 제어를 나타내는 두 개의 수영 레인으로 되어있다. 프로세스는 한 밴드에 대한 판매 리포트를 보는 밴드 매니저로 시작한다. 리포팅 툴은 사람이 관리하는 모든 밴드들을 검색하여 디스플레이하고 이중 한 개를 고를 것을 요청한다. 밴드 매니저가 한 밴드를 선택하면 리포팅 툴은 판매 정보를 검색하여 판매 리포트를 디스플레이 한다.




위로


컴포넌트 다이어그램

컴포넌트 다이어그램은 시스템을 물리적으로 볼 수 있도록 한다. 이것의 목적은 소프트웨어가 시스템의 다른 소프트웨어 컴포넌트들(예를 들어, 소프트웨어 라이브러리)에 대해 소프트웨어가 갖고 있는 종속 관계를 보여주는 것이다. 이 다이어그램은 매우 고급 레벨에서 볼 수 있거나 컴포넌트 패키지 레벨에서 볼 수 있다. 주 2

컴포넌트 다이어그램의 모델링은 이 예제에 잘 설명되어 있다. 그림 7은 네 개의 컴포넌트인 Reporting Tool, Billboard Service, Servlet 2.2 API, JDBC API를 보여주고 있다. Reporting Tool에서 출발하여 Billboard Service, Servlet 2.2 API, JDBC API로 가는 화살표는 Reporting Tool이 이들 세 개의 컴포넌트에 종속되어 있음을 나타낸다.

그림 7: 컴포넌트 다이어그램은 다양한 소프트웨어 컴포넌트들 간 종속관계를 보여준다.



위로


전개 다이어그램

전개 다이어그램은 하드웨어 환경에 시스템이 물리적으로 어떻게 전개되는지를 보여준다. 목적은 시스템의 다양한 컴포넌트들이 어디에서 실행되고 서로 어떻게 통신하는지를 보여주는 것이다. 다이어그램이 물리적 런타임을 모델링 하기 때문에 시스템 사용자는 이 다이어그램을 신중하게 사용해야 한다.

전개 다이어그램의 표기법에는 컴포넌트 다이어그램에서 사용되던 표기법 요소들이 포함된다. 이외에 노드 개념을 포함하여 두 가지 정도 추가되었다. 노드는 물리적 머신 또는 가상 머신 노드(메인프레임 노드)를 표현한다. 노드를 모델링 하려면 3차원 큐브를 그려 큐브 상단에 노드 이름을 적는다. 시퀀스 다이어그램에서 사용되던 네이밍 규칙([instance name] : [instance type]) (예, "w3reporting.myco.com : Application Server").

그림 8: 전개 다이어그램. Reporting Tool 컴포넌트가 WebSphere 내부에서 그려지기 때문에(w3.reporting.myco.com 노드의 내부에서 그려짐), 사용자들은 로컬 머신에서 실행되는 브라우저를 통해 Reporting Tool에 액세스 하면서 기업 인트라넷을 통해 HTTP에 연결할 수 있다는 것을 알 수 있다.

그림 8의 전개 다이어그램은 사용자가 로컬 머신에서 실행되고 기업의 인트라넷에서 HTTP를 통해 Reporting Tool에 연결되는 브라우저를 사용하여 Reporting Tool에 접근하는 것을 보여주고 있다. 이 툴은 물리적으로 w3reporting.myco.com 이라고 하는 Application Server에서 실행된다. 이 다이어그램은 IBM WebSphere 내부에서 그려진 Reporting Tool 컴포넌트를 보여준다. 이것은 결과적으로 node w3.reporting.myco.com에서 그려지게 되어있다. Reporting Tool은 자바를 사용하여 리포팅 데이터베이스를 IBM DB2의 JDBC 인터페이스에 연결하여 원시 DB2 통신을 사용하는 db1.myco.com 서버상에서 실행되는 실제 DB2 데이터베이스와 통신한다. 리포팅 데이터베이스와 통신하는 것 외에도 Report Tool 컴포넌트는 SOAP over HTTPS를 통해 Billboard Service와 통신한다.




위로


결론

이 글은 Unified Modeling Language에 대한 간단한 입문서에 불과하지만 여러분이 이 정보를 실제 프로젝트에 적용하거나 더 깊게 UML을 연구하기를 바란다. UML 다이어그램을 소프트웨어 개발 프로세스에 통합시키는 여러 소프트웨어 툴이 있지만, 자동화된 툴이 없더라도 화이트보드에 마커와 펜을 사용하여 UML 다이어그램을 그려도 좋다.




위로


1 상속과 기타 객체 지향 원리에 대한 기타 자세한 정보는 http://java.sun.com/docs/books/tutorial/java/concepts/inheritance.html을 참조하기 바란다.

2 컴포넌트 패키지 레벨은 프로그래밍 언어에 중립적인 방식으로 .NET의 네임스페이스(System.Web.UI)나 자바의 패키지(java.util) 같은 클래스 컨테이너 레벨을 참조하는 것이다.

2007/07/22 15:01 2007/07/22 15:01
이 글에는 트랙백을 보낼 수 없습니다
 세마포어(Semaphores)를 비록 IPC설비중의 하나로 분류하긴 했지만, 다른 파이프, 메시지큐, FIFO 등과는 좀다르다. 다른 IPC 설비들이 대부분 프로세스간 메시지 전송을 그 목적으로 하는데 반해서 세마포어는 프로세스간 데이타를 동기화 하고 보호하는데 그목적이 있다.

프로세스간 메시지 전송을 하거나, 혹은 공유메모리를 통해서 특정 데이타를 공유하게 될경우 발생하는 문제가, 공유된 자원에 여러개의 프로세스가 동시에 접근을 하면 안되며, 단지 한번에 하나의 프로세스만 접근 가능하도록 만들어줘야 할것이다. 이것은 쓰레드에서 메시지간 동기화를 위해서 mutex 를 사용하는것과 같은 이유이다.

하나의 데이타에 여러개의 프로세스가 관여할때 어떤 문제점이 발생할수 있는지 간단한 예를 들어보도록 하겠다.
int count=100; 
A 프로세스가 count 를 읽어들인다.     100
B 프로세스가 count 를 읽어들인다.     100
B 프로세스가 count 를 1 증가 시킨다.  101 
A 프로세스가 count 를 1 증가 시킨다.  101
count 는 공유자원(공유메모리 같은)이며 A와 B 프로그램이 여기에 대한 작업을 한다. A가 1을 증가 시키고 B가 1을 증가시키므로 최종 count 값은 102 가 되어야 할것이다. 그러나 A 가 작업을 마치기 전에 B가 작업을 하게 됨으로 엉뚱한 결과를 보여주게 되었다. 위의 문제를 해결하기 위해서는 count 에 A가 접근할때 B프로세스가 접근하지못하도록 block 시키고, A가 모든 작업을 마쳤을때 B프로세스가 작업을 할수 있도록 block 를 해제 시키면 될것이다.
우리는 세마포어를 이용해서 이러한 작업을 할수 있다. 한마디로 줄여서 세마포어는 "여러개의 프로세스에 의해서 공유된는 자원의 접근제어를 위한 도구" 이다.

세마포어의 작동원리

작동원리는 매우 간단하다. 차단을 원하는 자원에대해서 세마포어를 생성하면 해당자원을 가리키는 세마포어 값이 할당된다. 이 세마포어 값에는 현재 세마포어를 적용하고 있는 자원에 접근할수 있는 프로세스의 숫자를 나타낸다. 이 값이 0이면 이 자원에 접근할수 있는 프로세스의 숫자가 0이라는 뜻이며, 자원), 0보다 큰 정수면 해당 정수의 크기만큼의 프로세스가 자원에 접근할수 있다라는 뜻이 된다. 그러므로 우리는 접근제어를 해야하는 자원에 접근하기 전에 세마포어 값을 검사해서 값이 0이면 자원을 사용할수 있을때까지 기다리고, 0보다 더크면(1이라고 가정하자) 자원에 접근해서 세마포어 값을 1 감소 시켜서, 세마포어 값을 0으로 만들어서, 다른 프로세스가 자원에 접근할수 없도록 하고, 자원의 사용이 끝나면 세마포어 값을 다시 1증가시켜서 다른 프로세스가 자원을 사용할수 있도록 만들어주면 된다.

만약 세마포어 값을 검사했는데 세마포어 값이 0이라면 사용할수 있게 될때까지 (1이 될때까지) 기다리면 (block) 될것이다.

세마포어의 사용

세마포어의 사용은 위의 작동원리를 그대로 적용한다. 즉 1. 세마포어로 제어할 자원을 설정한다.
2. 해당 자원을 사용하기전에 세마포어 값을 확인한다.
3. 세마포어 값이 0보다 크면 자원을 사용하고, 세마포어 값을 1 감소 시킨다.
4. 세마포어 값이 0이면 값이 0보다 커질때까지 block 되며, 0보다 커지게 되면 2번 부터 시작하게 된다.

위의 작업을 위해서 Unix 는 다음과 같은 관련함수들을 제공한다.
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semget(key_t key, int nsems, int semflg);
int semop (int semid, struct sembuf *sops, unsigned nsops);
int semctl(int semid, int semnum, int cmd, union semun arg);

세마포어의 관리

세마포어는 그 특성상 원자화된 연산을 필요로 한다. 이러한 원자화된 연산은 유저레벨의 함수에서는 제공하기가 힘들므로, 세마포어 정보는 커널에서 전용 구조체를 이용해서 관리하게 된다. 다음은 커널에서 세모포어 정보를 유지하기 위해서 관리하는 구조체인 semid_ds 구조체의 모습이다. semid_ds 는 /usr/include/bits/sem.h 에 선언되어 있다. (이것은 리눅스에서의 경우로 Unix 버젼에 따라서 위치와 멤버변수에 약간씩 차이가 있을수 있다)
struct semid_ds
{
    struct ipc_perm sem_perm;     
    __time_t sem_otime;           
    unsigned long int __unused1;
    __time_t sem_ctime;           
    unsigned long int __unused2;
    unsigned long int sem_nsems;  
    unsigned long int __unused3;
    unsigned long int __unused4;
};
sem_perm 은 세마포어에 대한 퍼미션으로 일반 파일퍼미션과 마찬가지의 기능을 제공한다. 즉 현재 세마포어 구조체에 접근할수 있는 사용자권한을 설정한다. sem_nsems 는 생성할수 있는 세마포어의 크기이다. sem_otime 은 마지막으로 세마포어관련 작업을 한 시간(semop 함수를 이용)이며, sem_ctim 은 마지막으로 구조체 정보가 바뀐 시간이다.

semget 을 이용해서 세마포어를 만들자.

세마포어의 생성혹은 기존에 만들어져 있는 세마포어에 접근하기 위해서 유닉스에서 는 semget(2)를 제공한다. 첫번째 아규먼트는 세마포어의 유일한 키값을 위해서 사용하는 int 형의 키값이다. 우리는 이 key 값을 이용해서 유일한 세마포어를 생성하거나 접근할수 있게 된다. 새로 생성되거나 기존의 세마포어에 접근하거나 하는것은 semflg 를 통해서 제어할수 있다. 다음은 semflg 의 아규먼트이다.

IPC_CREAT
만약 커널에 해당 key 값으로 존재하는 세마포어가 없다면, 새로 생성 한다.
IPC_EXCL
IPC_CREAT와 함께 사용하며, 해당 key 값으로 세마포어가 이미 존재한다면 실패값을 리턴한다.
semflg 를 통해서 세마포어에 대한 퍼미션을 지정할수도 있다. 퍼미션 지정은 보통의 파일에 대해서 유저/그룹/other 에 대해서 지정하는것과 같다.

만약 IPC_CREAT 만 사용할경우 해당 key 값으로 존재하는 세마포어가 없다면, 새로 생성하고, 이미 존재한다면 존재하는 세마포어의 id 를 넘겨준다. IPC_EXCL을 사용하면 key 값으로 존재하는 세마포어가 없을경우 새로 생성되고, 이미 존재한다면 존재하는 id 값을 돌려주지 않고 실패값(-1)을 되돌려주고, errno 를 설정한다.

nsems 은 세마포어가 만들어질수 있는 크기이다. 이값은 최초 세마포어를 생성하는 생성자의 경우에 크기가 필요하다(보통 1). 그외에 세마포어에 접근해서 사용하는 소비자의 경우에는 세마포어를 만들지 않고 단지 접근만 할뿐임으로 크기는 0이 된다.

이상의 내용을 정리하면 semget 은 아래와 같이 사용할수 있을것이다.
만약 최초 생성이라면
    sem_num = 1;
그렇지 않고 만들어진 세마포어에 접근하는 것이라면
    sem_num = 0; 
sem_id = semget(12345, sem_num, IPC_CREAT|0660)) == -1)
{
    perror("semget error : ");
    return -1;
}
semget 은 성공할경우 int 형의 세마포어 지사자를 되돌려주며, 모든 세마포어에 대한 접근은 이 세마포어 지시자를 사용하게 된다.

위의 코드는 key 12345 를 이용해서 세마포어를 생성하며 퍼미션은 0660으로 설정된다. 세마포어의 크기는 1로 잡혀 있다(대부분의 경우 1). 만약 기존에 key 12345 로 이미 만들어진 세마포어가 있다면 새로 생성하지 않고 기존의 세마포어에 접근할수 있는 세마포어 지시자를 되돌려주게 되고, 커널은 semget 를 통해 넘어온 정보를 이용해서 semid_ds 구조체를 세팅한다.


예제: semget.c
#include <sys/types.h> 
#include <sys/ipc.h> 
#include <sys/sem.h> 

int main()
{
    int semid;
    semid = semget((key_t)12345, 1, 0666 | IPC_CREAT);
}
이제 위의 코드를 컴파일해서 실행시키고 나서 실제로 세마포어 정보가 어떻게 바뀌였는지 확인해 보도록 하자.

커널에서 관리되는 ipc 정보를 알아보기 위해서는 ipcs(8)라는 도구를 이용하면 된다.
[root@localhost test]# ipcs -s    
------ Semaphore Arrays --------
key        semid      owner      perms      nsems      status      
0x00003039 0          root      666        1         
0x00003039 은 key 12345 의 16진수 표현이다. 퍼미션은 666으로 되어 있고 semget 를 통해서 제대로 설정되어 있음을 알수 있다.

세마포어를 이용해서 접근제어 하기

이제 semget 을 통해서 세마포어를 만들거나 접근했으니, 이제 실제로 세마포어상태를 검사해서 접근제어를 해보도록하자.

이것은 semop를 통해서 이루어진다.

semop 의 첫번째 semid 는 semget 을 통해서 얻은 세마포어 지시자이다. 2번째 아규먼트는 struct sembuf 로써, 어떤 연산을 이루어지게 할런지 결정하기 위해서 사용된다. 구조체의 내은 다음과 같으며, sys/sem.h 에 선언되어 있다.
struct sembuf
{
    short sem_num;    // 세마포어의수
    short sem_op;     // 세마포어 연산지정
    short sem_flg;    // 연산옵션(flag)
}
sem_num 멤버는 세마포어의 수로 여러개의 세마포어를 사용하지 않는다면(즉 배열이 아닐경우) 0을 사용한다. 배열의 인덱스 사이즈라고 생각하면 될것이다. 보통의 경우 하나의 세마포어를 지정해서 사용하므로 0 이 될것이다.
sem_op 를 이용해서 실질적으로 세마포어 연산을 하게 되며, 이것을 이용해서 세마포어 값을 증가시키거나 감소 시킬수 잇다. sem_op 값이 양수일 경우는 자원을 다 썼으니, 세마포어 값을 증가시키겠다는 뜻이며, 음수일 경우에는 세마포어를 사용할것을 요청한다라는 뜻이다. 음수일 경우 세마포어값이 충분하다면 세마포어를 사용할수 있으며, 커널은 세마포어의 값을 음수의 크기의 절대값만큼을 세마포어에서 빼준다. 만약 세마포어의 값이 충분하지 않다면 세번째 아규먼트인 sem_flg 에 따라서 행동이 결정되는데,
sem_flg 가 IPC_NOWAIT로 명시되어 있다면, 해당영역에서 기다리지 않고(none block) 바로 에러코드를 리턴한다. 그렇지 않다면 세마포어를 획득할수 있을때까지 block 되게 된다. sem_flg 는 IPC_NOWAIT 와 SEM_UNDO 2개의 설정할수 있는 값을가지고 있다. IPC_NOWAIT 는 none block 모드 지정을 위해서 사용되며, SEM_UNDO 는 프로세스가 세마포어를 돌려주지 않고 종료해버릴경우 커널에서 알아서 세마포어 값을 조정(증가) 할수 있도록 만들어 준다.

설명이 아마 애매모호한면이 있을것이다. 간단한 상황을 예로 들어서 설명해 보겠다.
현재 세마포어 값이 1 이라고 가정하자. 
이때 A 프로세스가 semop 를 통해서 세마포어에 접근을 시도한다. 
A는 접근을 위해서 sem_op 에 -1 을 세팅한다. 즉 세마포어 자원을 1 만큼 사용하겠다라는 
뜻이다.   
현재 준비된 세마포어 값은 1로 즉시 사용할수 있으므로, 
A는 자원을 사용하게 되며, 커널은 세마포어 값을 1 만큼 감소시킨다. 

이때 B 라는 프로세스가 세마포어 자원을 1 만큼 사용하겠다라고 요청을 한다. 
그러나 지금 세마포어 값은 0 이므로 B는 지금당장 세마포어 를 사용할수 없으며, 
기다리거나, 에러값을 리턴 받아야 한다(IPC_NOWAIT).   
B는 자원 사용가능할때까지 기다리기로 결정을 했다.  

잠수후 A는 모든 작업을 다마쳤다. 
이제 세마포어를 되돌려줘야 한다. sem_op 에 1 을 세팅하면, 
커널은 세마포어 값을 1증가시키게 된다. 

드디어 기다리던 B가 세마포어 자원을 사용할수 있는 때가 도래했다. 
이제 세마포어 값은 1이 므로 B는 세마포어를 획득하게 된다.  
커널은 세마포어 값을 1 감소 시킨다.
B는 원하는 작업을 한다.
...
...

세마포어 조작

semctl 이란 함수를 이용해서 우리는 세마포어를 조정할수 있다. semctl 은 semid_ds 구조체를 변경함으로써 세마포어의 특성을 조정한다.

첫번째 아규먼트인 semid 는 세마포어 지시자이다. semnum 은 세마포어 배열을 다룰 경우 사용되며, 보통은 0이다. cmd 는 세마포어 조작명령어 셋으로 다음과 같은 조작명령어들을 가지고 있다. 아래는 그중 중요하다고 생각되는 것들만을 설명하였다. 더 자세한 내용은 semctl 에 대한 man 페이지를 참고하기 바란다.
IPC_STAT
세마포어 상태값을 얻어오기 위해 사용되며, 상태값은 arg 에 저장된다.
IPC_RMID
세마포어 를 삭제하기 위해서 사용한다.
IPC_SET
semid_ds 의 ipc_perm 정보를 변경함으로써 세마포어에 대한 권한을 변경한다.

예제를 통한 이해

지금까지 익혔던 내용을 토대로 간단한 예제프로그램을 만들어보겠다. 예제의 상황은 하나의 파일에 2개의 프로세스가 동시에 접근하고자 하는데에서 발생한다. 파일에는 count 숫자가 들어 있으며, 프로세스는 파일을 열어서 count 숫자를 읽어들이고, 여기에 1을 더해서 다시 저장하는 작업을한다. 이것을 세마포어를 통해서 제어하지 않으면 위에서 설명한문제가 발생할것이다.

위의 문제를 해결하기 위해서는 파일을 열기전에 세마포어를 설정해서 한번에 하나의 프로세스만 접근가능하도록 하면 될것이다. 모든 파일작업을 마치게 되면, 세마포어 자원을 돌려줌으로써, 비로서 다른 프로세스가 접근가능하게 만들어야 한다.
예제: sem_test
#include <sys/types.h> 
#include <sys/sem.h> 
#include <sys/ipc.h> 
#include <stdio.h> 
#include <unistd.h> 

#define SEMKEY 2345 

union semun
{
    int val;
    struct semid_ds *buf;
    unsigned short int *array;
};

static int  semid;
int main(int argc, char **argv)
{
    FILE* fp;
    char buf[11];
    char count[11];

    union semun sem_union; 

    // open 과 close 를 위한 sembuf 구조체를 정의한다. 
    struct sembuf mysem_open  = {0, -1, SEM_UNDO}; // 세마포어 얻기
    struct sembuf mysem_close = {0, 1, SEM_UNDO};  // 세마포어 돌려주기
    int sem_num;

    memset(buf, 0x00, 11);
    memset(count, 0x00, 11);

    // 아규먼트가 있으면 생성자
    // 그렇지 않으면 소비자이다.
    if (argc > 1)
        sem_num = 1;
    else 
        sem_num = 0;            

    // 세마포설정을 한다. 
    semid = semget((key_t)234, sem_num, 0660|IPC_CREAT);
    if (semid == -1)
    {
        perror("semget error ");
        exit(0);
    }    

    // counter.txt 파일을 열기 위해서 세마포어검사를한다. 
    if(semop(semid, &mysem_open, 1) == -1)
    {
        perror("semop error ");
        exit(0);
    }

    if ((fp = fopen("counter.txt", "r+")) == NULL)
    {
        perror("fopen error ");
        exit(0);
    }
    // 파일의 내용을 읽은후 파일을 처음으로 되돌린다.  
    fgets(buf, 11, fp);
    rewind(fp);

    // 개행문자를 제거한다. 
    buf[strlen(buf) - 1] = 0x00;

    sprintf(count, "%d\n", atoi(buf) + 1); 
    printf("%s", count);
    // 10초를 잠들고 난후 count 를 파일에 쓴다. 
    sleep(10);
    fputs(count,fp);

    fclose(fp);
    // 모든 작업을 마쳤다면 세마포어 자원을 되될려준다
    semop(semid, &mysem_close, 1);
    return 1;
}


코드는 매우 간단하지만, 세마포어에 대한 기본적인 이해를 충분히 할수 있을만한 코드이다. 생성자와 소비자의 분리는 프로그램에 넘겨지는 아규먼트를 이용했다. 모든 작업을 마치면 테스트를 위해서 10초를 기다린후에 세마포어를 돌려주도록 코딩되어 있다.

우선 count 를 저장할 파일 counter.txt 를 만들고 여기에는 1을 저장해 놓는다. 그다음 ./sem_test 를 실행시키는데, 최초에는 생성자를 만들어야 하므로 아규먼트를 주어서 실행시키고, 그다음에 실행시킬때는 소비자가 되므로 아규먼트 없이 실행하도록 하자. 다음은 테스트 방법이다.
[root@coco test]# ./sem_test 1&
[1] 3473
36
[root@coco test]# ./sem_test
위 코드를 실행해보면 ./sem_test 1 이 세마포어자원을 돌려주기 전까지 ./sem_test 가 해당영역에서(세마포어 요청하는 부분) 블럭되어 있음을 알수 있고, 충돌없이 count가 잘되는것을 볼수 있을것이다.

세마포어는 커널에서 관리하는데 세마포어를 사용하는 프로세스가 없다고 하더라도 semctl 을 이용해서 제거하지 않는한은 커널에 남아있게 된다. 세마포어 정보를 제거하기 위해서는 semctl 연산을 하든지, 컴퓨터를 리붓 시커거나, ipcrm(8)이란 도구를 사용해서 제거시켜 줘야 한다.

결론

세마포어는 fcntl 과 매우 비슷한 일을 수행하는데, 좀더 세밀한 조정이 가능하다라는 장점을 가지고 있지만 간단한 일을 하기엔 지나치게 복잡한 면이 없잖아 있다. 이럴경우에는 세마포어 대신 fcntl 을 사용하자.
2007/07/20 13:38 2007/07/20 13:38
이 글에는 트랙백을 보낼 수 없습니다

3. XML Parser - rssParser.js

 

먼저의 post에서 var xml = rssParser(req.responseXML); 를 보셨을 것입니다.
req.responseXML
을 받아서 JSON으로 파싱 하는 것입니다.
이부분은 rssParser.js javascript파일로 따로 구현해 보았습니다.

 

 

먼저 전체 소스를 보시죠.

 

  0: /**

  1:  *  @(#)rssParser.js    V0.1    2007/03/15

  2:  *

  3:  *  rss XML Parser extend Prototype.js v1.5

  4:  *  Copyright 2005-2007 by VRICKS, All Right Reserved.

  5:  *  http://www.vricks.com

  6:  *

  7:  *  GNU LESSER GENERAL PUBLIC LICENSE

  8:  *  TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  9:  *

 10:  *  @Author Woo-Chang Yang, routine@vrick.com

 11:  */

 12:

 13: function rssParser(xml) {

 14:     var v = Try.these(

 15:         // Rss ¹öÀüÀ» °¡Á® ¿É´Ï´Ù.

 16:         function() {

 17:             return xml.getElementsByTagName("rss")[0].getAttribute("version") ? "2.0" : false;

 18:         },

 19:         function() {

 20:             return xml.getElementsByTagName("rdf:RDF")[0].getAttribute("xmlns") ? "1.0" : false;

 21:         },

 22:         function() {

 23:             return xml.getElementsByTagName("feed")[0].getAttribute("xmlns") ? "atom" : false;

 24:         }

 25:     )

 26:     switch(v) {

 27:         case "2.0"  : return new rssParser2(xml); break;

 28:         case "1.0"  : return new rssParser1(xml); break;

 29:         case "atom" : return new rssParserAtom(xml); break;

 30:         default     : return false;

 31:     }

 32: };

 33:

 34: // Rss 2.0 Calss

 35: var rssParser2 = Class.create();

 36: Object.extend(rssParser2.prototype, {

 37:     initialize    : function(xml) {

 38:         var channel = xml.getElementsByTagName("channel")[0];

 39:         this.title  = channel.getElementsByTagName("title")[0].firstChild.nodeValue;

 40:         this.link   = channel.getElementsByTagName("link")[0].firstChild.nodeValue;

 41:         if(channel.getElementsByTagName("image")[0]) {

 42:             var images   = channel.getElementsByTagName("image")[0];

 43:             this.image   = {

 44:                 "url"    : images.getElementsByTagName("url")[0].firstChild.nodeValue,

 45:                 "title"  : images.getElementsByTagName("title")[0].firstChild.nodeValue,

 46:                 "link"   : images.getElementsByTagName("link")[0].firstChild.nodeValue

 47:             };

 48:         }

 49:         else {

 50:             this.image = {

 51:                 "url"    : "",

 52:                 "title"  : "",

 53:                 "link"   : ""

 54:             }

 55:         }

 56:         this.description = Try.these (

 57:             function() {

 58:                 return channel.getElementsByTagName("description")[0].firstChild.nodeValue;

 59:             },

 60:             function() { return "" }

 61:         );

 62:         this.language    = Try.these (

 63:             function() {

 64:                 return channel.getElementsByTagName("language")[0].firstChild.nodeValue;

 65:             },

 66:             function() {

 67:                 return channel.getElementsByTagName("dc:language")[0].firstChild.nodeValue;

 68:             },

 69:             function() { return ""}

 70:         );

 71:         this.pubDate     = Try.these(

 72:             function() {

 73:                 return channel.getElementsByTagName("pubDate")[0].firstChild.nodeValue;

 74:             },

 75:             function() {

 76:                 return channel.getElementsByTagName("lastBuildDate")[0].firstChild.nodeValue;

 77:             },

 78:             function() { return ""; }

 79:         );

 80:         var items = new Array();

 81:         $A(channel.getElementsByTagName("item")).each(function(i){

 82:             items.push({

 83:                 "category" : Try.these(

 84:                     function() {

 85:                         return i.getElementsByTagName("category")[0].firstChild.nodeValue;

 86:                     },

 87:                     function() { return "" }

 88:                 ),

 89:                 "title" : i.getElementsByTagName("title")[0].firstChild.nodeValue,

 90:                 "link"    : i.getElementsByTagName("link")[0].firstChild.nodeValue,

 91:                 "description" : Try.these (

 92:                     function() {

 93:                         return i.getElementsByTagName("description")[0].firstChild.nodeValue;

 94:                     },

 95:                     function() { return "" }

 96:                 ),

 97:                 "pubDate" : Try.these (

 98:                     function() {

 99:                         return i.getElementsByTagName("pubDate")[0].firstChild.nodeValue;

100:                     },

101:                     function() {

102:                         return i.getElementsByTagName("dc:date")[0].firstChild.nodeValue;

103:                     },

104:                     function() { return "" }

105:                 )

106:             })

107:         })

108:         this.item = items;

109:     }

110: });

111:

112: var rssParser1 = Class.create();

113: Object.extend(rssParser1.prototype, {

114:     initialize    : function(xml) {

115:         var channel = xml.getElementsByTagName("channel")[0];

116:         var images  = xml.getElementsByTagName("image")[0];

117:         this.title  = channel.getElementsByTagName("title")[0].firstChild.nodeValue;

118:         this.link   = channel.getElementsByTagName("link")[0].firstChild.nodeValue;

119:         this.image  = {

120:             "url"   : images.getElementsByTagName("url")[0].firstChild.nodeValue,

121:             "title" : images.getElementsByTagName("title")[0].firstChild.nodeValue,

122:             "link"  : images.getElementsByTagName("link")[0].firstChild.nodeValue

123:         };

124:         this.description = channel.getElementsByTagName("description")[0].firstChild.nodeValue;

125:         this.language    = channel.getElementsByTagName("dc:language")[0].firstChild.nodeValue;

126:         this.pubDate     = channel.getElementsByTagName("dc:date")[0].firstChild.nodeValue;

127:         var items        = xml.getElementsByTagName("item");

128:         var itemValue    = new Array();

129:         for(var i = 0; i < items.length; i++) {

130:             itemValue.push({

131:                 "category"   : items[i].getElementsByTagName("category")[0].firstChild.nodeValue,

132:                 "title"      : items[i].getElementsByTagName("title")[0].firstChild.nodeValue,

133:                 "link"       : items[i].getElementsByTagName("link")[0].firstChild.nodeValue,

134:                 "description":

135:                               items[i].getElementsByTagName("description")[0].firstChild.nodeValue,

136:                 "pubDate"    : items[i].getElementsByTagName("dc:date")[0].firstChild.nodeValue

137:             });

138:         };

139:         this.item = itemValue;

140:     }

141: });

142:

143: var rssParserAtom = Class.create();

144: Object.extend(rssParserAtom.prototype, {

145:     initialize    : function(xml) {

146:         this.title   = xml.getElementsByTagName("title")[0].firstChild.nodeValue;

147:         this.link    = xml.getElementsByTagName("link")[0].getAttribute("href");

148:         this.image   = {

149:             "url"    : "",

150:             "title"  : "",

151:             "link"   : ""

152:         };

153:         this.description = xml.getElementsByTagName("info")[0].firstChild.nodeValue;

154:         this.language    = "";

155:         this.pubDate     = xml.getElementsByTagName("modified")[0].firstChild.nodeValue;

156:         var items        = xml.getElementsByTagName("entry");

157:         var itemValue    = new Array();

158:         for(var i = 0; i < items.length; i++) {

159:             itemValue.push({

160:                 "category"   : "",

161:                 "title"      : items[i].getElementsByTagName("title")[0].firstChild.nodeValue,

162:                 "link"       : items[i].getElementsByTagName("link")[0].getAttribute("href"),

163:                 "description":items[i].getElementsByTagName("summary")[0].firstChild.nodeValue,

164:                 "pubDate"    : items[i].getElementsByTagName("created")[0].firstChild.nodeValue

165:             });

166:         };

167:         this.item = itemValue;

168:     }

169: });

 

function rssParser(xml) {...}을 보시면 Rss의 버전을 구해서 알맞은 Class를 호출 하는 부분 입니다.
Try.these (...)
부분이 보이실 것입니다. API 보기
아래에 나열된 function()을 수행하여 먼저 성공한 하나만을 호출 합니다. 정말 멋진 생각입니다. ^^;

var rssParser2 = Class.create();
Object.extend(rssParser2.prototype, { ... }
새로운 Class를 생성하여 prototype을 확장 하였습니다.
Class.create()
로 확장을 하면 항상 initialize 를 수행 합니다.
new rssParser2(xml);
하게 되면 자동으로 initialize 부분이 수행이 된다는 의미 입니다.

var channel = xml.getElementsByTagName("channel")[0];
태그명 channel의 첫번째 element를 가져 옵니다. channel이 한개뿐인데 배열로 가져 왔습니다.
태그명으로 가져 올 때는 getElementsByTagName 여기서도 보실수 있듯이 복수형입니다.
그래서 항상 배열형태로 리턴이 됩니다. [0] 이분이 빠지면 에러가 납니다. ^^;

이제 channel의 자식 노드들을 뽑아올 차례 입니다.

this.title  = channel.getElementsByTagName("title")[0].firstChild.nodeValue;
<channel>
아래에 있는 태그명 <title>의 첫번째 노드의 value를 가져 옵니다.
DOM
에 대한 자세한 내용은
URL : http://www.ibm.com/developerworks/kr/library/wa-ajaxintro5/
을 참고 하시기 바랍니다.

이렇게 title, lilnk, descriiption을 가져옵니다.
pubDate
의 경우는 먼저 pubDate 태그를 찾은 후 없으면 lastBuildDate를 그것도 없으면 공백문자를 리턴해 줍니다.
뭐 어려운거 없습니다. 그냥 DOM으로 노드 뽑아 오듯이 하나씩 뽑아서 대입해 주면 됩니다.

<item>태그에 들어있는 각각의 post를 가져올 차례입니다.
$A(channel.getElementsByTagName("item")).each(function(i){ ... })
$A
Array의 확장판으로 Ruby틱한 배열형태를 사용할 수 있도록 해 줍니다. API 보기
<item>
을 돌면서 <item>에 포함한 <title>, <link>, <description>, <pubDate>를 가져와서 JSON형태로 파싱하고
파싱된 것들을 item이란 변수에 배열로 담아 놓습니다.
이렇게 해 놓으면 후에 item[i].title, item[i].link, item[i].description 으로 가져올 수 있습니다.


어떻게 글로 설명을 할려니 더 어려워 진거 같네요.
구현된 모습을 한번 보겠습니다.

 
 
 
얼추 비슷합니다. ^^V
이것으로 이번 post는 마치겠습니다.
 
내용도 없고 재미도 없는 글 읽어 주셔서 감사합니다.
 
 

시간이 되면 rssParser.js를 이용한 구글 개인화 페이지를 구현해 보도록 하겠습니다.
물론 prototype.js를 이용할 것이며 Scriptaculous 의 이펙트도 이용할 것입니다.
화면상으로 잠시 맛배기를 ^^;
 
 



2007/07/20 09:59 2007/07/20 09:59
이 글에는 트랙백을 보낼 수 없습니다

2. Rss XML로 가져오기

 

이제 가져올 element는 정해 졌습니다.
하지만 한가지 문제가 있습니다. 이놈을 어떻게 가져 올 것인가???

 

Ajax라고 많이 들어 보셨을 것입니다. 일명 HTML, Javascript DHTML, DOM으로 구성된 비동기 통신을 가리키는 말입니다.
Ajax
의 핵심에는 XMLHttpRequest 가 있습니다. 대충 보니 HTTP프로토콜을 이용하여 XML로 어떤 값을 받는거 같습니다
.
이놈이 사용자가 눈치채지 못하게 어떤 값을 가져오고 그것을 DOM형태로 소리 소문 없이 문서 사이에 끼워 넣으면서 여러가지 동적인 화면을 구성할 수 있습니다.


Ajax
에 대해서 더 자세히 아시고 싶으신 분은
URL :
http://www.ibm.com/developerworks/kr/library/wa-ajaxintro1.html
를 참고해 주세요.
Post의 주제가 초간단 Rss Reader를 만드는 것이기 때문에 주제에 충실 하도록 하겠습니다.

 

 

이제 보실 부분은 rssReader.htmljavascript 부분입니다.
3개의 function으로 구성되어 있습니다.

 

uriChk()
입력한 URL값이 유효한 값인지 검사 합니다. 최소한 http://XXX.XXX https://XXX.XXX 형태가 되는지 검사합니다.
여기를 통과하면 일단 유효한 URL이라고 판단하여 getRss(URL)을 호출 합니다.


getRss(URL)
이부분이 핵심입니다. 해당 URL Ajax비동기 통신을 하여 xml을 가져 옵니다.
그리고 var xml = rssParser(req.responseXML); xml JSON으로 파싱을 시킵니다
.
rssParser
rssParser.js include javascript에 들어 있습니다
.
만약 알맞게 JSON으로 파싱이 되었다면 printHTML(JSON) 을 호출 합니다.


printHTML(JSON)
이 부분은 그냥 이뿌게 꾸며서 <div id="title"></div> 여기에 제목 부분을 넣고
<div id="contents"></div>
여기에 item 리스트를 넣는 것입니다.

 

 

전체 소스를 보겠습니다.

 

 0: <?xml version="1.0" encoding="EUC-KR" ?>
 1: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 2: <html xmlns="http://www.w3.org/1999/xhtml">
 3: <head>
 4: <meta http-equiv="Content-Type" content="text/html; charset=EUC-KR" />
 5: <style type="text/css">
 6:     *         {font-size:12px;}
 7:     body      {margin:0px; padding:0px;color:#666666;}
 8:     a:active  {color:#236fa5; text-decoration:none;}
 9:     a:visited {color:#236fa5; text-decoration:none;}
10:     a:link    {color:#236fa5; text-decoration:none;}
11:     a:hover   {color:#518fbb; text-decoration:underline;}
12: </style>
13: <script type="text/javascript" language="javascript" src="./prototype.js"></script>
14: <script type="text/javascript" language="javascript" src="./rssParser.js"></script>
15: <script type="text/javascript" language="javascript">
16: <!--
17:     function uriChk() {
18:         var uri = $("uri");
19:         if(uri.value == "") {
20:             alert("가져올 피드의 주소를 입력 하세요.");
21:             uri.focus();
22:             return;
23:         }
24:         if(!(/(http|https):\/\/\w+([-.]\w+).+$/).test(uri.value)) {
25:             alert("피드 주소가 유효하지 않습니다.");
26:             uri.focus();
27:             return;
28:         }
29:         getRss(uri.value);
30:     }
31: 
32:     function getRss(uri) {
33:         Element.hide("title");
34:         $("contents").innerHTML = $("loading").innerHTML;
35:         new Ajax.Request(
36:             uri,
37:             {
38:                 method : "get",
39:                 onSuccess : function(req) {
40:                     var xml = rssParser(req.responseXML);
41:                     if(!xml) { 
                            alert("Rss
포멧이 아닙니다.");
                            $("contents").innerHTML = "";
                            return;
                        }
42:                     printHtml(xml);
43:                 },
44:                 onFailure : function() {
45:                     alert("데이타를 가져오는데 실패 했습니다.");
46:                     $("contents").innerHTML = "";
47:                 }
48:             }
49:         );
50:     }
51: 
52:     function printHtml(xml) {
53:         var d = new Date(xml.pubDate);
54:         var title = '';
55:         var cont = '';
56:         title += '<a href="' + xml.link + '" target="_blank"><strong style="font-size:16px;">' 
                  + xml.title + '</strong></a>';
57:         if(d != 'NaN') {
58:             title += '<span style="padding-left:20px;">(' + d.toLocaleString() + ')</span>';
59:         }
60:         title += '<br/><br/>' + xml.description;
61:         for(var i = 0; i < xml.item.length; i++) {
62:             cont += '<a href="' + xml.item[i].link +'" target="_blank">'
                     + '<strong style="font-size:16px;">' + xml.item[i].title + '</strong></a>';
63:             cont += '<hr style="height:1px;color:518fbb"/>';
64:             if(xml.item[i].pubDate != "") {
65:                 var pd = new Date(xml.item[i].pubDate);
66:                 cont += '<span style="color:#aaaaaa;">' + pd.toLocaleString() 
                         + '</span><br/><br/>';
67:             }
68:             cont += xml.item[i].description.replace("\n", "<br/>") + '<br/><br/><br/>';
69:         };
70:         $("title").innerHTML = title;
71:         $("contents").innerHTML = cont;
72:         Element.show("title");
73:     }
74: //-->
75: </script>
76: <title>Rss Reader V 0.1 by Vricks - www.vricks.com</title>
77: </head>
78: <body>
79: <div style="width:800px;text-align:left;margin:auto;">
80:     <div style="background-color:#ff000a;padding:16px 0 50px 16px;margin:20px 0;">
81:         <strong style="font-size:14px;color:#ffffff;">
82:             Rss Reader V0.1
83:         </strong>
84:     </div>
85:     <div style="text-align:center;margin:20px;">
86:         <input type="text" id="uri" value="http://blog.rss.naver.com/jeffyang.xml" 
             style="width:360px;" />
87:         <input type="button" value="RSS 가져오기" onclick="uriChk();" />
88:     </div>
89:     <div id="title" 
             style="padding:20px;background-color:#f7f7f4;border:1px solid #aaaaaa;display:none;"
>
        </
div>
90:     <div id="contents" style="padding:20px;"></div>
91:     <div style="display:none;" id="loading"><div style="text-align:center;">
             <
img src="./loading.gif" alt=""
/>
        </
div></div>
92: </div>
93: </body>
94: </html>

$("uri") -> API 보기
document.getElementById("uri")
와 같은 말입니다. 물론 $()함수는 더 많은 기능을 제공해 줍니다.

Element.hide("title"); -> API 보기
document.getElementById("title").style.display = "";
과 같은 말입니다. 물론 Element.hide("title") 함수가 더 많은 기능을 제공합니다.

 

var myAjax = new Ajax.Request(
    url,
    {
        method: 'get',
        parameters: params,
        onSuccess: function(req) {
            something...
        },
        onFailure: function() {
            something....
        }
    }  
);

XMLHttpRequest
를 쉽게 구현해 주는 부분입니다. -> API 보기
url :
접속할 URL입니다. 뒤에 오는 method get일 경우 get방식의 prameters를 붙여서 사용할 수 있습니다.
) var url = "http://test.com/test.xml?id=1234&date=2007-03-01";
method : get/post
가 올 수 있습니다
.
parameters : get
방식의 경우 null 이 옵니다. post의 경우는 ?를 제외한 prameter를 붙여서 사용 합니다
.
) var params = "id=1234&date=2007-03-01";
onSuccess :
성공 했을때의 코드를 기술 합니다
.
onFailure :
실패 했을때의 코드를 기술 합니다.


printHTML()
JSON으로 파싱된 값들을 받아서 뿌려 주는 일을 합니다.
var xml = rssParser(req.responseXML);
로 받아서 printHTML(xml)로 보냈습니다
.
channel
title을 가져 올 때는 단순히
xml.title
item[0]
title을 가져 올 때는 xml.item[0].title 이렇게 가져 오면 됩니다
.
rssParser.js
는 다음 post에서 만나 보실 수 있습니다.

 

2007/07/20 09:58 2007/07/20 09:58
이 글에는 트랙백을 보낼 수 없습니다

언제부터인지 우리 주위에 Rss라는 단어가 많이 익숙해져 있습니다.
블로그, 뉴스, 도서 사이트 심지어 UCC까지 WEB2.0시대인 요즘은 원하는 정보를 Rss 서비스로 받아 보실 수 있습니다
.
이러한 Rss 피드를 모아서 편하게 구독하게 해 주는 것이 일명 Rss Reader입니다
.
Rss Reader
에는 웹 기반의 Rss Reader, 설치형 Rss Reader, 동기화 가능 설치형 Rss Reader이 있습니다
.
이러한 것들을 구현하기 위해서는 수집, 정렬, 검색, 동기화 등등 많은 기술들이 들어 갑니다
.
하지만 버뜨~ post의 제목이 초간단 Rss Reader입니다
.
제목에 충실히 인터넷 연결이 되어 있고 IE가 깔려 있으면(흠 이부분은 나중에 이야기 드릴께요 ㅠㅠ) 단순히 해당 피드 주소만 넣으면 읽기만 가능한 그러한 Reader를 만들어 보겠습니다
.
누군가 그랬습니다
. Simple is The Best....
사실 실력이 요까지라서 더 복잡한건 ..... OTL

 

초간단 Rss Reader를 구현하기 위한 기본적인 지식으로 Javascript, html, xml의 기본적인 지식이 필요합니다.
Javascript
의 경우 prototype.js를 기본으로 하여 이미 구현한 라이브러리를 최대한 이용하도록 하겠습니다
.
prototype.js
URL : http://www.prototypejs.org 에서 다운 받으실 수 있으며

http://blog.naver.com/jeffyang/150015619963
에 가시면(post 자랑 ^^;) 한글로 된 메뉴얼의 링크를 얻으실 수 있습니다.
Post 작성하는 순간에 버전이 1.51 RC1으로 올랐습니다
.
이번 버전에는 XML노드를 처리하기 위한 라이브러리가 보이는 군요
.
애써 외면하며 여기서는 prototype 1.5를 기준으로 하겠습니다.

 

간략한 post의 순서입니다.

 

1. Rss를 분석하여 어떤 elements를 가지고 표현할 것인지 결정 합니다.
2. Rss
XML로 받아 옵니다. 이 부분은 Javascript XMLHttpRequest객체를 가지고 구현을 합니다
.
3.
받아온 XML을 파싱해야 합니다. 이 부분 역시 Javascript JSON 포멧으로 파싱 합니다
.
4. JSON
을 받아서 화면이 html코드로 이쁘게 뿌려 주면 간단 RssReader의 완성 입니다.


Javascript, html, xml
로만 하겠다고 해 놓고 JSON이란 놈이 보이는 군요.
JSON
JavaScript Object Notation 라 불리우는 경량의 DATA 교환 형식입니다
.
Javascript
맞죠!!! 네 분명 Javascript Object라고 했습니다. JSON에 대해서 더 자세히 알고 싶으신 분은

URL : http://json.org/json-ko.html
을 참고 하세요. 한글로 잘 설명되어 있습니다.
평소 영어를 외계어로 여기는 까닭에 해석하시는 수고를 덜어 드리고자 이렇게 친절하게 한글주소로 안내를 해 드립니다.


이쯤에서 첫번째 주제 Rss를 분석해 보도록 하겠습니다.


현재 국내에서 가장 많이 사용되고 있는 것이 Rss 2.0 포멧입니다. 그리고 Rss 1.0, ATOM 포멧도 많이 보입니다.
솔직히 웬만한 사이트들은 모두 Rss 2.0을 지원해 줍니다. 하지만 국내 사이트들 중에는 무늬만 Rss 2.0이고 표준을 지키지 않은 곳들이 많이 있습니다
.
표준을 준수하지 않은 경우는 해당 Rss서비스에 맞도록 하나씩 처리를 해 줘야 합니다
.
필수 Elements가 빠진곳이나 엉뚱한 Elements가 나타나는 곳들도 종종 있었습니다
.
한국에서 구글이 힘을 못쓰는 이유는 포탈 및 대형 사이트 들의 비표준이 지대한 공헌을 했다고 하더군요. (공감 100% 입니다
.)
어찌 되었건 한국어로 된 Rss 서비스를 받고자 한다면 이 정도 수고는 해야 하는군요. ㅠㅠ

이러한 비표준을 처리하기 위해서 prototype.js Try.these 구문을 이용 했습니다.

 

1. RSS 2.0 분석

 

Rss란 무엇인가?
한국정보통신기술위원회의 문서를 빌리자면

 

RSS 는 웹 컨텐츠 신디케이션 형식으로 Really Simple Syndication 의 약자이다.
컨텐츠 신디케이션(Content Syndication)이란 사이트 컨텐츠의 일부 또는 전체를 다른 서비스에서 이용할 수 있도록 해주는 것을 말한다
.
신디케이션된 컨텐츠(또는 feed)는 컨텐츠 자체와 메타데이타 로 구성된다. 이러한 feed 에는 헤드라인 내용만 있을 수도 있고, 스토리에 대한 링크만 있을 수도 있으며, 사이트의 전체 컨텐츠가 포함될 수도 있다
.
근래에 들어 특히 1 인 미디어인 블로그(blog)를 중심으로 컨텐츠 신디케이션 표준인 RSS 의 가능성을 확인되면서, 다양한 응용들이 등장하고 있고, 차세대 웹 콘텐츠 유통 기술의 핵심 표준으로 주목받고 있다
.


뭔 소린지 잘 모르겠습니다. 그냥 구조를 한번 살펴 보죠.
대략 다음과 같은 구조로 되어 있습니다. 이중 굵게 처리된 것들은 필수 요소 입니다
.
item
태그의 title, link, description은 모두 필수는 아니고 세개 중 한개는 반드시 있어야 합니다.

 

 0: <?xml version="1.0" encoding="euc-kr" ?>
 1: <rss version="2.0">
 2:     <channel>
 3:         <title>채널의 제목</title>
 4:         <link>채널의 링크</link>
 5:         <description>채널에 대한 간략한 설명</description>
 6:         <language>채널이 작성되는 언어</language>
 7:         <copyright>채널의 저작권 통지</copyright>
 8:         <managingEditor>채널 편집 담당자의 전자우편</managingEditor>
 9:         <webMaster>채널 웹마스타의 전자우편</webMaster>
10:         <pubDate>채널 컨텐츠의 발행 시간</pubDate>
11:         <lastBuildDate>채널이 마지막으로 변경된 시간</lastBuildDate>
12:         <category>채널이 속해있는 가테고리</category>
13:         <generator>채널을 생성하는데 사용된 프로그램</generator>
14:         <images>
15:             <url>이미지의 URL</url>
16:             <title>이미지의 제목</title>
17:             <link>해당 사이트의 링크</link>
18:             <width>이미지 넓이</width>
19:             <height>이미지 높이</height>
20:         </images>
21:         <item>
22:             <title>항목의 제목</title>
23:             <link>항목의 URL</link>
24:             <description>항목의 내용</description>
25:             <author>저작자의 전자우편</author>
26:             <category>항목이 속해있는 가테고리</category>
27:             <comments>항목과 관련된 설명 페이지의 URL</comments>
28:             <guid>고유하게 항목을 식별할 수 있는 문자열</guid>
29:             <pubDate>항목 발행 시간</pubDate>
30:         </item>
31:     </channel>
32: </rss>

 

어렵습니다. ㅠㅠ.
이것만 봐서는 눈에 잘 안 들어 옵니다. 그래서 IE 7.0의 피드랑 비교해서 각 항목을 알아 보겠습니다.

 

 

<IE 7.0 피드 그림>

 

 

대충 감이 옵니다.
화면을 보아 하니 <channel>에서 시작해서 </channel>로 닫히는군요
.
<channel>
아래에 있는 <link><title><pubDate>도 대충 어떤 놈인지 알겠습니다
.
중간에 <image>도 보이구 <category>도 보입니다
.
<item>
에서 시작해서 </item>까지가 한개의 post 로 구분되어 있습니다
.
<item>
아래에는 <link><title><pubDate><description>이 보이네요..

이제 감 잡았습니다.
가져올 elements를 구분하겠습니다
.
피드의 titlelink
, pubDate(없으면 lastBuildDate)를 가져와서 있는놈들을 큰 제목에 뿌려 주고

item
을 배열로 돌리면서 item의 자식들 link, title, pubDate, desciption 중 있는놈들을 모두 뿌려 주겠습니다.


2007/07/20 09:57 2007/07/20 09:57
이 글에는 트랙백을 보낼 수 없습니다
HTML  2007/07/19 17:40

TABLE FRAME 속성

[Syntax]

<table frame="void">
참고로 border를 1이상으로 사용해야 함.

 

[아래 예제 소스]
<table width="100%" border="1" cellspacing="0" cellpadding="2" frame="xxxxxx">
.......
</table>
void : 바깥쪽 전체 테두리 지우기
FRAME Attribute Property
     
     
above : 윗쪽 테두리만 보이기
FRAME Attribute Property
     
     
below : 아래쪽 테두리만 보이기
FRAME Attribute Property
     
     
border : 전체 테두리 보이기
FRAME Attribute Property
     
     
box : 전체 테두리 보이기
FRAME Attribute Property
     
     
hsides : 윗쪽, 아래쪽 테두리만 보이기
FRAME Attribute Property
     
     
lhs : 왼쪽 테두리만 보이기
FRAME Attribute Property
     
     
rhs : 오른쪽 테두리만 보이기
FRAME Attribute Property
     
     
vsides : 왼쪽, 오른쪽 테두리만 보이기
FRAME Attribute Property

   
     
↓ 아래는 응용해서 만들어본 테이블입니다.
FRAME Attribute Property
1
2 3
     
FRAME Attribute Property

   
     
FRAME Attribute Property

   
     
2007/07/19 17:40 2007/07/19 17:40
이 글에는 트랙백을 보낼 수 없습니다
IRC채널에 올라온 글을 읽던 도중 후배가 int main()와 void main()의 차이점을 물어보았다.

2학년때 C스터디를 진행할 때 이것땜에 곤란을 겪었던 적이 한번 있었는데, 그때는 설렁 설렁 넘어갔던 기억이 있다.

고급계산이론 과제를 하던 도중에 한번 쉬엄쉬엄 찾아볼까 해서 구글링을 해봤는데 알아낸 사실은 다음과 같다.

1. C99 이후의 표준 main 함수는 다음과 같아야 한다.
int main() 이거나
int main(int argc, char *argv[])

2. void main()에 비해서 int main()이 좋은 점은 종료 후 invoker(호출자)에게 해당 값을 넘겨준다. 이는 exit(); 함수와 같은 역할인듯 하다.

3. 다음과 같은 방법으로 바로 직전에 실행 되었던 프로그램이 반환한 값을 알 수 가 있었고, 실제로 넘겨준다는 것을 알 수 있었다.

다음과 같은 소스 코드를 작성한다.
#include <cstdio>

int main() {
        return 1;
}
컴파일 후 다음과 같이 해본다.
[libe@aslab libe]$ ./a.out
[libe@aslab libe]$ echo $?
1

근데 이것은 linux상에서 확인한 결과이고, 실제로 windows상에서는 어떤 방식으로 확인을 할 수 있을지 궁금하다(아시는 분께서 답변을 해주시면 감사하겠습니다.). 요사이 관심사 중에 하나가 run-time error 체킹을 쉽게 하는 방법에는 무엇이 있을까? 였는데, linux상에서 테스트 해본 결과 assert를 이용한 run-time error의 경우 138, segmentation fault의 경우 139라는 값을 확인 할 수 있었다. 생각 했던 것 보다 쉽다 ~_~

첨가 - windows의 경우
구글링을 해본 결과 Windows에서도 다음과 같은 batch file을 생성하면 확인을 할 수 있다.
@echo off
실행파일명
@if "%ERRORLEVEL%" == "0" goto good

:fail
echo Execution Failed
echo return value = %ERRORLEVEL%
goto end

:good
echo Execution Succeded
echo return value = %ERRORLEVEL%
goto end

:end
하지만 linux와 다른점은 assert()의 경우엔 얌전히 동작하는데, scanf("%d",a); 와 같은 코드에서 오류창을 띄운다음에 아마 메모리 주소로 추정되는 값을 내뱉는다-_-; 조금더 연구해봐야할 필요가 있을듯 싶다.

그런데 exit를 쓰면 void main()도 마찬가지의 기능을 할 수 있을텐데(테스트 결과도 마찬가지 였다.)  int main()의 좋은점이라고 하면 cstdlib헤더를 첨가 하지 않아도 된다는 점이라고 해야하나?

2007/07/10 18:01 2007/07/10 18:01
이 글에는 트랙백을 보낼 수 없습니다
Linux/SHELL  2007/07/04 21:21

* 문서
1. 패스워드 관리장부
2. 서버자원관리부
3. 백업관리장부
4. 서버작업이력부
5. 서버점검이력부

* 절대로 서버를 믿어서는 안된다. 여러분 자신을 믿으셔야 합니다.

* 리눅스 배포판
http://www.redhat.com/
http://www.debian.org/
http://www.slackware.com/
http://www.linux-mandrake.com/
http://www.wowlinux.com/
http://www.alzzalinux.com/
http://www.suse.com/
http://www.haansoftlinux.com/
http://www.hancom.com/
http://www.sulinux.net/


* 한글 사용
# cat /etc/sysconfig/i18n
LANG="ko_KR.eucKR"
SUPPORTED="en_US.UTF-8:en_US:en:ko_KR.eucKR:ko_KR:ko"
SYSFONT="lat0-sun16"
SYSFONTACM="8859-15"

* 네트워크 통신테스트 단계
1. gateway까지 통신에 이상이 없는가?
2. 외부망까지의 통신에 이상이 없는가?
3. DNS까지의 통신에 이상이 없는가?
4. 도메인을 통한 외부망까지의 통신에 이상이 없는가?

* 게이트웨어 설정
route add default gw 게이트웨이_IP주소 dev 네트워크_인터페이스_장치명
예) # route add default gw 192.168.0.1 dev eth0
예) # ifconfig eth0 192.168.1.1 netmask 255.255.255.0 broadcast 192.168.1.255 up
    # route add -net 192.168.1.0 netmask 255.255.255.0 eth0
    # route add default gw 192.168.1.254 dev eth0

* route 명령을 통한 routing 규칙
# route
Kernel IP routing table
Destination Gateway  Genmask  Flags Metric Ref Use Iface
192.168.0.0 *  255.255.255.0 U 0 0 0 eth0
169.254.0.0 *  255.255.0.0 U 0 0 0 eth0
127.0.0.0 *  255.0.0.0 U 0 0 0 lo
default  192.168.1.254 0.0.0.0  UG 0 0 0 eth0
1. 위의 첫번째행부터 다음행으로 차례대로 한 행씩 읽어 들여서 처리할 퍀시을 보낼 수 있는가를 확인합니다.
2. default라는 것이 이 서버의 기본 게이트웨이 설정해을 의미하며 그 행의 Gateway 항목값이 기본게이트웨이의 IP주소입니다. default는 0.0.0.0을 의미합니다.
3. 보낼 데이터의 목적지 IP주소와 Genmask값(SubnetMask)과의 AND연산을 해서 그 결과가 해당행의 Destination항목과 동일할 겨웅에 해당 행의 Iface에 있는 인터페이스로 해당 패킷을 전송하게 됩니다.
4. Iface항목의 값이 lo인 행(Destination값이 127.0.0.0인 행)은 이 서버의 루프백(loopback) 데이터를 처리하기 위한 설정입니다. 즉, 자기 자신에게로 데이터를 보낼 때에 처리를 담당하는 인터페이스입니다.

* 기본게이트웨이 정보:
# cat /etc/sysconfig/network
NETWORKING=yes   # 통신이 되도록 지원할 것인가의 여부
HOSTNAME=ns.superuser.co.kr # 서버의 호스트네임(hostname)
GATEWAY=192.168.1.254  # 시스템 전체에 대한 Global한 기본게이트웨이
GATEWAYDEV=   # 기본게이트웨이 장치명
FORWARD_IPV4=no   # 리눅스 서버가 라우터역할을 할 것인가의 여부. 즉, 패킷포워딩을 수행학자 할 경우에는 yes로 설정하고 패킷포워딩을 하지 않을 경우에는 no로 설정한다. 일반적이 서버역할을 수행할 경우에는 거의 대부분 no로 설정한다.

* IP관련 네트워크 정보:
# cat /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0   # 네트워크 디바이스 장치명
BOOTPROTO=static  # 이 네트워크 디바이스에 유동IP를 동적(dynamic)으로 할당하고자 할 경우에는 dhcp를 주소 정적(static)한 고정IP를 할당하고자 할 경우에는 static이라는 값을 준다.
IPADDR=192.168.1.101  # 이 서버의 IP주소
BROADCAST=192.168.1.255  # 브로드캐스트 주소
NETMASK=255.255.255.0  # 넷마스크값
NETWORK=192.168.1.0  # 네트워크주소
ONBOOT=yes   # 해당 네트워크 인터페이스(eth0)를 서버 부팅시에 활성화하여 사용할 것인가를 결정한다.
#USERCTL=no
#GATEWAY   # 해당 네트워크 인터페이스(eth0)의 기본 게이트웨이를 설정. /etc/sysconfig/network 에 있는 GATEWAY보다 우선순위가 높다.

* 네임서버관련 정보:
# cat /etc/resolv.conf
search superuser.co.kr
nameserver 168.126.63.1
nameserver 164.124.101.2


* ifconfig 명령어
1. eth0 인터페이스 down/up
# ifconfig eth0 down
# ifconfig eth0 up
2. 네트워크 설정
- 설정할 network device : eth0
- 호스트 IP Address     : 192.168.0.100
- Netmask               : 255.255.255.0
- Broadcast IP          : 192.168.0.255
- Network IP            : 192.168.0.0
# ifconfig eth0 192.168.0.100 netmask 192.168.0.0 broadcast 192.168.0.255 up

* 네트워크 주소 설정파일
- /etc/sysconfig/network
- /etc/sysconfig/network-scripts/ifcfg-eth0
- /etc/rc.d/init.d/network start|restart|stop

* 리부팅
# reboot
# shutdown -r now
# init 6

* IP앨리어싱
# cp /etc/sysconfig/network-scripts/ifcfg-eth0 /etc/sysconfig/network-scripts/ifcfg-eth0:0
# cp /etc/sysconfig/network-scripts/ifcfg-eth0 /etc/sysconfig/network-scripts/ifcfg-eth0:1
# cp /etc/sysconfig/network-scripts/ifcfg-eth0 /etc/sysconfig/network-scripts/ifcfg-eth0:2
# cp /etc/sysconfig/network-scripts/ifcfg-eth0 /etc/sysconfig/network-scripts/ifcfg-eth0:3
# 각 파일 편집
# cat /etc/sysconfig/network-scripts/ifcfg-eth0:0
DEVICE=eth0:0   # -> 이 부분 수정 (원래 eth0)
BOOTPROTO=static
BROADCAST=192.168.0.255
IPADDR=192.168.0.211  # -> 이 부분 수정 (원래 192.168.0.210)
NETMASK=255.255.255.0
NETWORK=192.168.0.0
ONBOOT=yes
TYPE=Ethernet
# cat /etc/sysconfig/network-scripts/ifcfg-eth0:1
DEVICE=eth0:1   # -> 이 부분 수정 (원래 eth0)
BOOTPROTO=static
BROADCAST=192.168.0.255
IPADDR=192.168.0.212  # -> 이 부분 수정 (원래 192.168.0.210)
NETMASK=255.255.255.0
NETWORK=192.168.0.0
ONBOOT=yes
TYPE=Ethernet
# /etc/rc.d/init.d/network restart

* hostname 변경
- 영구적으로 변경
# vi /etc/sysconfig/network -> HOSTNAME 수정 (재부팅 필요)
- 일시적으로 변경
# hostname hostname.you_want.com -> 지금 즉시 수정

* netstat
- a 옵션 : --all과 같으며 listen되는 소ㅔㅅ정보와 listen되지 않는 소켓정보 모두를 보여줌.
- n 옵션 : --numeric과 같으며 10진수의 수치정보로 결과를 출력해줌.
- r 옵션 : --route과 같으며 설정된 라우팅정보를 출력해줌
- p 옵션 : --program과 같으며 실행되고 있는 각 프로그램과 PID정보를 출력함.
- i 옵션 : --interface=iface과 같으며 모든 네트워ㅡ인터페이스정보를 출력함. 또는 특정 네트워크인터페이스를 지정할 수도 있음.
- c 옵션 : --continuous과 같으며 netstat결과를 연속적으로 출력함.
- l 옵션 : --listening과 같으며 현재 listen되고 있는 소켓정보를 출력
- s 옵션 : --statistics과 같으며 각 프로토콜에 대한 통계정보를 출력
- t 옵션 : --tcp와 같으며 tcp 정보를 출력
- u 옵션 : --udp와 같으며 udp 정보를 출력

* netstat 연결상태
- LISTEN : 연결이 가능하도록 관련데몬이 떠있으며 연결이 가능함을 나타냄.
- SYN-SENT : 연결을 요청한 상태
- SYN_RECEIVED : 연결요구에 의한 응답을 준 후에 확인메시지를 기다리고 있는 상태.
- ESTABLISHED : 앞의 3단계 연결과정이 모두 종료된 후에 연결이 완료된 상태
- FIN-WAIT1, CLOSE-WAIT, FIN-WAIT2 : 연결종료를 위해 종료 요청을 받은 후의 종료 과정임.
- CLOSING : 전송된 메시지가 유실된 상태를 나타냄.
- TIME-WAIT : 연결종료 후에 한동안 유지하고 있는 상태.
- CLOSED : 연결이 완전히 종료됨.

* 국내도메인 사용기관 정보 조회
리눅스 쉘         : whois -h whois.krnic.net 대상도메인(or IP Address)
                    ex) whois -h whois.krnic.net 202.86.12.129
웹에서의 조회 URL : http://whois.nic.or.kr/

* 국제도메인 사용기관 정보 조회
리눅스 쉘         : whois -h whois.internic.net 대상도메인(or IP Address)
                    ex) whois -h whois.internic.net cnn.com
웹에서의 조회 URL : http://www.networksolutions.com/en_US/whois/index.jhtml

* ethtool (이더넷카드의 설정의 상황출력과 변경을 하는 명령어)
- 이더넷 설정확인 : ethtool eth0
- 이더넷 설정변경 : ethtool -s eth0 [speed 10|100|1000] [duplex half|full] [autoneg on|off]
# ethtool eth1
# ethtool -s eth1 speed 100 duplex full autoneg off

* mii-tool (Media Independent Interface Tool)
# mii-tool
eth0: no autonegotiation, 10baseT-HD, link ok
eth1: 100 Mbit, full duplex, no link
# mii-tool eth0
eth0: no autonegotiation, 10baseT-HD, link ok
# mii-tool eth1
eth1: 100 Mbit, full duplex, no link
# mii-tool -v eth0
eth0: no autonegotiation, 10baseT-HD, link ok
  product info: Intel 82555 rev 4
  basic mode: autonegotiation enabled
  basic status: autonegotiation complete, link ok
  capabilities: 100baseTx-FD 100baseTx-HD 10baseTx-FD 10baseTx-HD
  advertising: 100baseTx-FD 100baseTx-HD 10baseTx-FD 10baseTx-HD flow-control
  link partner: 10baseT-HD
# mii-tool -R -> 네트워크 인터페이스의 기본설정내용대로 재설정
# mii-tool -r -> 네트워크 인터페이스의 autonegotiation설정을 재시작

* modprobe를 이용한 랜카드 모드 설정확인 및 변경하기
# lsmod
Module  Size Used by
eepro100 18144 1
# cat /etc/modules.conf
alias eth0 eepro100
alias scsi_hostadapter aic7xxx
# modprobe 랜카드모듈명 speed_duplex=설정번호
- speed_duplex=0 : auto detection 모드로 설정
- speed_duplex=1 : 10M, half duplex로 설정
- speed_duplex=2 : 10M, full duplex로 설정
- speed_duplex=3 : 100M, half duplex로 설정
- speed_duplex=4 : 100M, full duplex로 설정
ex) modprobe eepro100 speed_duplex=1

* tcpdump
# tcpdump -i eth0  -> 특정 ethernt(eth0) 으로 송수신 되는 데이터 패킷 덤프하여 확인
# tcpdump -i eth0 -w TCPDUMP -> 특정 ethernet으로 송수신 되는 패킷들 파일에 저장 및 확인
# tcpdump -r TCPDUMP  -> TCPDUMP에 저장된 패킷헤드들을 확인
# tcpdump -i eth0 -c 10  -> 특정 ethernet에서 지정한 개수만큼의 네트워크 패킷 덤프하여 확인
# tcpdump -w tcpdump.log -s 1500 tcp port 22 and host 192.168.0.1
    -> 서버의 특정포트로 송수신되는 모든 데이터패킷 전체를 확인
    -> 이 명령의 의미는 현재 로컬서버와  192.168.0.00서버사이의 통신데이터패킷 중 tcp 22번포트의 모든 패킷을 1500길이로 캡쳐하여 tcpdump.log파일에 저장
  -w tcpdump.log 결과를 tcpdump.log파일에 저장
  -s 1500  캡쳐할 패킷의 길이로서 1500은 패킷의 전체길이를 의미하므로 모든 패킷을 캡쳐하게 된다.
  tcp port 22  캡쳐할 대상 프로토콜과 포트를 지정한 것으로 TCP 포트 22번으로 송수신되는 데이터를 캡쳐하다.
  host 192.168.0.100 192.168.0.100서버와 송수신되는 데이터를 대상으로 캡쳐한다.
# tcpdum -Xqnr tcpdump.log -> 캡쳐한 tcpdump.log파일의 내용을 ASCII모드로 확인


*** 리눅스 서버관리 유틸리티 ***

* 시스템 설정과 셋업 유틸리티
# setup
시스템설정과 셋업 파일들을 한데 모은 유틸리티.

* 리눅스 인증설정 유틸리티(NIS와 Shadow패스워드를 설정하는 텍스트 모드 도구)
# authconfig
# systemp-config-authentication
# pwconv -> shadow 패스워드 사용
# pwunconv -> shadow 패스워드 사용
MD5 Passwords 를 사용해서 문자수를 최대 256byte까지 사용할 수 있다.

* 시스템 날짜와 시간을 수정하는 그래픽 인터페이스
# redhat-config-date
# system-config-date
#system-config-time

* httpd 웹 서버에 대한 그래픽 설정 도구
# redhat-cofig-httpd
# system-config-http

* 키보드를 수정하는데 사용되는 그래픽 인터페이스
# redhat-config-keyboard
# system-config-keyboard

* 언어를 수정하는데 사용되는 그래픽 인터페이스
# redhat-config-language
# system-config-language

* 마우스 설정 그래픽 인터페이스
redhat-config-mouse
system-config-mouse

* Red Hat Linux 네트워크 설정 도구에 사용되는 그래픽 사용자 인터페이스. (참고: )
# netconfig
# redhat-config-network
# system-config-network

* NFS 서버 설정 도구
# redhat-config-nfs
# system-config-nfs

* Red Hat Linux에 사용되는 패키지 관리자
# redhat-config-packages
# system-config-packages

* 프린타 설정 백엔드/프론트엔드 조합
# printconf
# redhat-config-printer
# system-config-printer

* 루트 암호를 수정하는데 사용되는 그래픽 인터페이스
# redhat-config-rootpassword
# system-config-rootpasswd

* Samba 서버 설정 도구
# redhat-config-samba
# system-config-samba

* 시스템 보안 수준을 수정하는데 사용되는 그래픽 인터페이스 (lokkit 유틸리티와 같은 기능)
# redhat-config-securitylevel
# system-config-securitylevel

* redhat-config-services는  initscript와 xinetd 설정 유틸리티
# chkconfig
# ntsysv
# redhat-config-services
# system-config-services
이 유틸리티에서 설정가능한 서비스들
- amanda : 서버클라이언트 환경의 네트워크 백업시스템
- amd  : 자동마우트서비스(automatically mount file system)
- anacron : 주기적인 작업을 설정할 수 있는 서비스
- apmd  : 전원관리 데몬. 즉 전원감시 데몬
- arpwath : 네트워크인터페이스 하드웨어주소(MAC주소)와 IP주소를 쌍으로 모니터링하는 툴로서 보안도구로도 사용됨.
- atd  : 지정된 시간에 특정 작업을 실행할 수 있는 프로그램.
- bootparamd : 디스크가 없는 서버의 부팅정보제공을 하는 데몬
- chargen : character generate 서비스(보안에 취약함)
- chargen-udp : character generate 서비스(udp)
- comsat : 메일도착알림이나 사용자 알림사항등이 있을 때 알려주는 biff server
- crond  : 주기적인 작업실행을 할 수 있는 cron데몬
- daytime : daytime 서비스
- daytime-udp : daytime udp 서비스
- dhcpd  : ADSL과 MODEM사용자들에게 IP를 할당해주는 DHCP서버데몬
- finger : 로컬이나 원격서버의 계정사용자 정보 확인하는 finger서비스.
- gated  : 라우팅 데몬
- gpm  : 문자의 복사나 붙일 수 있는 가상콘솔을 위한 마우스서버
- gssftp : GSS FTP 서버데몬 프로그램
- identd : TCP/IP IDENT 프로토콜 서버 프로그램.
- imapd  : POP과 함께 메일수신데몬으로 사용되는 IMAP데몬
- imaps  : SSL을 지원하는 IMAP데몬
- innd  : 인터넷 NEWS 데몬
- ipchains : IP firewall 관리데몬 프로그램
- ipop3  : 서버에서 클라이언트로 메일을 보내주는 POP서버
- iptables : IP 패킷필터링 관리 프로그램.
- kadmin : kerberos V5 데이터베이스 관리 프로그램
- keytable : 키보드설정 데몬 프로그램
- kprop  : 마스터 KDB와 복제를 동기화시키는 서버유티리티
- kudzu  : 하드웨어 장치설정 프로그램
- ldap  : 디렉토리서비스 엑세스를 위한 클라이언트-서버 프로토콜 데몬
- linuxconf : 리눅스설정 유틸리티인 linuxconfi데몬 프로그램.
- lpd  : 프린터서버 데몬 프로그램.
- mysqld : Mysql데이터베이스 데몬 프로그램.
- named  : DNS데몬 프로그램
- nessusd : 보안프로그램인 nessus 데몬프로그램.
- netfs  : 네트워크 파일시스템을 지원하기 위한 데몬프로그램.
- network : 네트워크 설정 제어 프로그램
- nfs  : network file system 데몬 프로그램.
- nfslock : NFS지원을 위한 데몬 프로그램.
- nscd  : Name Service Cache 데몬 프로그램.
- ntalk  : 네트워크상에서 talk로 대화를 할 수 있는 데몬프로그램.
- ntpd  : Network Time Protocol(NTP) 데몬 프로그램.
- pop3s  : SSL을 지원하는 POP3S 데몬 프로그램
- portmap : rpc를 TCP/IP포트로 변환하는 rpc portmapper
- proftpd : ftp데몬의 일종인 proftp데몬 프로그램.
- rarpd  : RARP(Reverse Address Resolution Protocol)데몬 프로그램.
- routed : 라우팅데몬 프로그램
- rstatd : rpc rstatd 데몬 프로그램(kernel statistics server)
- rsync  : 디스크동기화나 네트워크백업을 위한 rsync 데몬 프로그램
- rusersd : rpc rusers 데몬 프로그램
- rwalld : 로그인한 사용자에게 메시지를 보내는 rpc rwalld 데몬
- rwhod  : 서버에 로그링ㄴ한 사용자 확인(rpc rwhod 데몬 프로그램)
- sendmail : sendmail 데몬 프로그램
- smb  ; 삼바(samba)데몬 프로그램
- snmpd  : 네트워크관리 프로토콜인 snmpd 데몬 프로그램.
- sshd  : Secure Shell(SSH) 데몬 프로그램
- swat  : 삼바(samba) 웹관리툴 데몬 프로그램
- syslog : 로그시스템 데몬 프로그램
- telnet : 텔넷(telnet)데몬 프로그램
- webmin : 웹에서 시스템관리를 할 수 있는 webmin 데몬 프로그램.
- xfs  : X윈도우를 위한 폰트(font)서버 데몬
- xinetd : 인터넷수퍼데몬 xinetd 데몬 프로그램.
- ypbind : NIS 바인딩 데몬 프로그램
- yppaswdd : NIS서버/클라이언트간 패스워드갱신 데몬
- ypserv : NIS 서버데몬 프로그램.

* 사운드카드를 감지하고 설정하는 그래픽 인터페이스
# redhat-config-soundcard
# system-config-soundcard

* 사용자와 집단들을 관리하느데 사용되는 그래픽 인터페이스
# redhat-config-users
# system-config-users

* 시스템 모니터 셋팅 프로그램
# sytem-config-display


* 서버관리자의 역할
1. 현재 운영중인 서버의 하드웨어와 운영체제에서 가질 수 있는 최대의 성능을 낼 수 있도록 "시스템을 최적화" 해야합니다.
2. 주기적이고 반복적인 작업들은 쉘프로그램이나 C, Perl 등의 언어를 사용하여 "반복업무의 업무자동화"를 해야합니다.
3. 서버 운영체제와 사용자의 데이터보호를 위하여 "확실한 보안대비"를 구축하고 있어야 합니다.
4. "체계적이고 주기적인 백업정책"을 적용하고 있어야 합니다.
5. 서버 장애발생시에 가장 빠르게 복구가 가능하도록 "빠른 복구정책"을 마련하고 있어야 합니다.

* 리눅스 서버의 종료 및 재시작
- shutdown을 이용한 시스템 종료 및 재시작
# /sbin/shutdown [-t sec] [-rkhncfF] time [warning-messages]
  -k : 실제로 종료하지 않고 모든 사용자에게 경고 메시지만을 보냅니다.
  -r : 시스템 종료후에 재부팅을 합니다.(reboot)
  -h : 시스템을 종료하며 재부팅을 하지 않습니다.(halt)
  -f : 재부팅할 때 파일시스템체ㅡ를 (fsck: file system check)를 하지 않습니다.
  -c : 이전에 내렸던 shutdown명령을 취소합니다.
  time : 몇분 후에 시스템을 종료할 것인가를 지정합니다.
  warning-messages : 사용자에게 보내질 종료 메시지.
# shutdown -h 5 "system rebooting..." -> "system rebooting..." 메시를 뿌리면서 현재부터 5분 후에 시스템을 종료
# shutdown -c -> shutdown 종료명령 취소
# shutdown -r +5 "system rebooting..." -> 5분 후에 시스템을 재시작
# shutdown -r now -> 시스템을 즉시 재부팅
# shutdown -h now -> 시스템을 즉시 종료
- reboot(= shutdown -r now)으로 서버 재시작하기
- poweroff로 서버 종료하기
- halt(= shutdown -h now)로 서버 종료하기
- init을 원하는 실행레벨로 서버 종료 및 재시작
레벨0 : 시스템종료(init 0, 그리고 halt와 shutdown -h now와 동일함)
레벨1 : 싱글모드(관리모드), ㅗㄴ솔로서만 접근가능하며 수퍼유저(root)만이 사용할 수 있다. 시스템관리나 root패스워드를 벼경하고자할 때에 사용함(init 1)
레벨2 : NFS를 지원하지않는 멀티유저 실행모드(init 2)
레벨3 : NFS를 지원하는 멀티유저 실행모드(init 3)
레벨4 : 사용하지않는 실행레벨(사용자가 지정하여 사용할 수 있음)
레벨5 : X윈도우 환경으로 실행된 멀티유저 시행모드(init 5)
레벨6 : 서버 재부팅되는 실행모드(init 6, reboot, shutdown -r now)

* CDROM 마운트
# mount -t iso9660 /dev/hdc /media/cdrom
# mount -t iso9660 /dev/cdrom /mnt/cdrom
# umount /media/cdrom

* 리눅스 복구(부팅)디스켓 제작하기
1. mkbootdis
# mkbootdisk --device /dev/fd0 2.6.9-.667smp
2. dd
# fdformat /dev/fd0H1440
# dd if=/boot/vmlinuz-2.6.9-1.667 of=/dev/fd0
# rdev /dev/fd0 /dev/hda1

* 리눅스 디렉토리 구조
/ : 최상의 디렉토리인 루트디렉토리
/bin : 중요하고 꼭 필요한 명령어가 있는 디렉토리
/boot : 커널(vmlinux등) 시스템 부팅에 관련된 파일을 저장하고 있는 디렉토리.
/dev : 시스템 디바이스(device)파일을 저장하고 있는 디렉토리.
/etc : 패스워드파일등 시스템의 전체 환경설정파일을 저장하고 있는 디렉토리.
/home : 사용자의 ㅎㅁ디렉토리, 대부분 ID와 동일한 이름의 디렉토리를 가짐.
/lib : 프로그램(C, C++등)에 필요한 각종 라이브러리를 저장
/media : 플로피, CD-ROM등 마운트를 위한 디렉토리.(/mnt in redhat9)
/proc : 실행중인 프로세스나 현재 시스템정보를 파일형태로 저장하고 있는 가상디렉토리.
/root : root의 홈디렉토리.
/sbin : 시스템 관리자용 명령어를 저장하고 있는 디렉토리.
/tmp : 일시적인 저장을 위한 디렉토리.
/usr : 각종 어플리케이션등이 설치되어 있는 디렉토리.
/var : 시스템운용 중에 생성되었다가 삭제되는 데이터를 일시적 저장을 위한 디렉토리.
/usr/bin : 일반사용자들이 사용가능한 명령어 파일들이 존재하는 디렉토리.
/usr/X11R6 : X 윈도우 시스템의 루트 디렉토리.
/usr/include : C 프로그램에 필요한 헤드파일(*.h) 디렉토리.
/usr/lib : /lib에 들어가지 않은 라이브러리 디렉토리.
/usr/man : 명령어들의 도움말을 주는 매뉴얼(manual)페이지 디렉토리.
/usr/sbin : /bin에 제외된 명령어와 네트워크관련 명령어가 들어있는 디렉토리.
/usr/src : 프로그램 소스(주로 커널소스)가 저장되는 디렉토리.
/usr/local : MYSQL, Apache등과 같은 어플리케이션들이 설치되는 장소.
/var/log : 각종 로그파일(messages 파일등)이 저장되는 디렉토리.
/var/spool/mail : 각 계정사용자들의 메일파일이 저장되는 디렉토리
/var/spool/lpd : 프린트를 하기 위한 임시 디렉토리(스풀링 디렉토리).
/var/spool/mqueue : 발송을 위한 메일 일시저장 디렉토리
/var/spool/cron : 각 사용자들의 cron 설정파일들이 저장된 디렉토리
/var/spool/at : atd 즉, 예약작업에 간한 파일들이 저장되는 디렉토리
/var/ftp : FTP서비스를 위한 다운로드될 파일들 즉, FTP홈디렉토리
/var/www : RPM으로 설치된 아파치의 홈페이지 파일들이 저장되는 디렉토리 즉, 아파치 홈페이지 디렉토리
/var/named : BIND 즉, DNS에서 사용하는 zone파일들이 저장되는 디렉토리
/etc/mail : sendmail.cf나 access파일등의 sendmail의 설정파일들이 존재하는 디렉토리
/etc/ssh : SSH서비스, 즉 sshd데몬에서 사용하는 각종 설정파일들이 존재하는 디렉토리
/etc/squid : squid 프락시서버의 설정파일들이 저장된 디렉토리
/etc/samba : 삼바관련 설정파일들이 저장된 디렉토리
/etc/skel : 계정사용자 생성시의 초기화파일들이 저장된 디렉토리(useradd에서 사용함)
/etc/rd.d : 부팅레벨별 부팅스크립트파일들이 존재하는 디렉토리
/etc/pam.d : PAM설정 정보파일들이 저장된 디렉토리
/etc/httpd : RPM으로 설치된 아파치 설정파일(httpd.conf등)들이 저장된 디렉토리

* 리눅스 부팅과정
1. 전원스위치 ON
2. BIOS프로그램 실행
3. ROM-BIOS의 POST(자체진단기능) 수행
4. ROM-BIOS가 부트로더(GRUB)를 불러들임
5. ROM-BIOS의 실행종료와 GRUB의 실행시작
   검색된 부팅매체의 0번 섹터(대부분 MBR 이라고 부름)에서 Boot Program을 읽어들여 메모리로 적재(로드, load)합니다.
6. GRUB의 실행초기 단계
   GRUB의 메뉴, 입력대기상태
7. GRUB에 의한 부팅메뉴 선택
   여기서 선택한 부팅메뉴로 GRUB은 부팅을 계속해 나갑니다.
   GRUB 설정 파일(/boot/grub/grub.conf, /boot/grub/menu.lst)
8. GRUB이 커널이미지를 메모리로 적재와 swapper 호출
   GRUB은 입력된 커널이미지를 메모리로 불러들입니다.(적재, load) 메모리로 load된 커널은 swapper를 호출합니다.
9. swapper의 장치 드라이브 초기화
   GRUB에 의해 swapper라는 pid 0번인 프로세스가 실행이 되며 swapper는 각 장치 드라이브들을 초기화 합니다.
10. swapper의 init프로세스 실행
    swapper라는 프로세스튼 다시 pid 1인 init프로세스(/sbin/init)를 실행하게 됩니다. 즉, swapper가 init프로세스를 실행시키고 본연의 swapper로서의 기능을 수행하기 시작합니다.
11. init프로세스의 /etc/inittab파일 호출
    init프로세스는 /etc/inittab파일을 읽어들입니다. 특, init프로세스는 제일먼저 /etc/inittab 파일을 읽어서 init프로세스가 무엇을 해야할 것이가를 결정하게 됩니다.
    /etc/inittab파일내의 각 행의 포맷
    name: level-number:options:process -options
    세번째 항목인 options 에 올 수 있는 것들
    - respawn  : 프로세스가 종료될때마다 재실행됩니다.
    - wait  : 지정된 프로세스가 실행되면 init은 프로세스가 종료될 때까지 기다린 후에 다음 작업으로 넘가도록 하는 옵션입니다.
    - once  : 해당 프로세스를 한번만 실행되도록 하는 옵션입니다.
    - boot  : 시스템이 부팅되는 동안 해당 프로세스가 실행되며, init은 실행레벨필드를 무시합니다.
    - bootwait  : 시스템이 부팅되는 동안 해당 프로세스가 실행되며, init은 프로세스가 종료될 때까지 기다립니다.
    - off  : 아무런 동작도 발생하지 않도록 하는 옵션입니다.
    - ondemand  : 실행레벨이 1, 2, 3인 경우에만 ㅇ용합니다. init은 이 세개의 실행레벨과 함께 호울될 때만 프로세스가 작동합니다. 잘 사용되지 않는 옵션입니다.
    - initdefault : 시스템이 부팅되면서 어떤 실행레벨로 부팅을 할 것인가를 결정하게 하는 옵션입니다. 즉, 시스템의 실행레벨을 결정하는 옵션입니다.
    - sysinit  : 시스템이 부티오디는 동안 한번만 프로세스가 실행하는 옵션이니다. sysinit은 boot나 bootwait보다 우선권을 가집니다.
    - powerwait  : init은 SIGPWR신호를 받는 경우에만 프로세스가 가동됩니다.
    - powerfail  : powerwait과 같지만, init은 프로세스가 돤력도ㅚㄹ 때까지 기다리지 않습니다.
    - powerokwait : init가 SIGPWR신호를 받고 /etc/powerstatus라는 텍스트 파일에 OK라는 문자열이 있을 때 프로세스가 실행됩니다.
    - ctrlaltdel : 이 옵션은 init프로세스가 SIGINT신호를 받았을 때에 싱행되록 합니다.
    - kbrequest  : 이 옵션은 init프로세스가 키보드관리자로부터 keyboardsignal을 받았을 때에 해당프로세스를 실행되도록 합니다.
12. init에 의해 /etc/rc.d/rc.sysinit 실행
    /etc/rc.d/rc.sysinit 파일은 실행레벨가는 무관하게 부팅과 함께 일회만 실행이 됩니다.
    /etc/rc.d/rc.sysinit 파일의 역할
13. init에 의해 각 부팅 레벨별 스크립트 실행
    앞 단계에서 결정된 실행레벨에 따라서 설정되어 있는 모든 프로세스들을 실행하게 됩니다.
14. init에 의해 /etc/rc.d/rc.local 파일 실행
15. init에 의해 /sbin/update 실행
16. init에 의해 CRTL+ALT+DEL키 작동 설정
17. init에 의해 전원관리 설정
18. init에 의해 6개의 가상콘솔 실행
    콘솔 로그인을 위해 /sbin/mingetty를 실행합니다.
19. init에 의해 콘솔(첫번째 가상콘솔)로그인 프롬프트 실행됨

* 사용자 관리
1. 사용자 생성
# useradd bible
# passwd bible
# grep bible /etc/passwd
bible:x:500:500:/home/bible:/bin/bash
# grep bible /etc/shadow
bible:$akdsjflkjalkdjfakdjfa;dkjfqpijfa:12784:0:99999:7:::
# grep bible /etc/group
bible:x:500:
# ls /home/bible
# ls -l /var/spool/mail/bible
-rw-rw---- 1 bible mail 0 Jan 1 13:46 bible
# useradd -d /user/bible2 -u 1000 -s /bin/sh bible2
# grep bible2 /etc/passwd
bible2:x:1000:1000::/user/bible2:/bin/sh
# grep bible2 /etc/shadow
bible2:!!:12784:0:99999:7:::
# grep bible2 /etc/group
bible2:x:1000
# ls /user/bible2
# useradd -d /user/bible3 -u 2000 -s /bin/csh -c parkSungSoo -e 2011-12-3 -p 88888888 bible3
  -d /user/bible3 : 홈디렉토리 위치를 /user/bible3으로 지정함
  -u 2000  : UID를 2000으로 지정함
  -s /bin/csh  : bible3 사용자가 기본으로 사용할 쉘종류를 C쉘로 지정함
  -c ParkSungSoo : 계정사용자의 간단한 코멘트문(주로 이름이나 사무실명등을 지정함)
  -e 2011-12-31  : bible3 사용자의 계정사용 종료일자를 2011년 12월 31일로 지정함
  -p 88888888  : bible3사용자의 기본 패스워드를 88888888로 지정함
  bible3  : 생성할 계정명(bible3)을 지정함
# grep bible3 /etc/passwd
bible3:x:2000:2000:ParkSungSoo:/user/bible3:/bin/csh
# grep bible2 /etc/shadow
bible2:88888888:12784:0:99999:7::15339:
# grep bible2 /etc/group
bible3:x:2000
# ls /user/bible3
- useradd 명령이 참조하는 파일들
  a. /etc/default/useradd 파일
     이 파일은 useradd 명령어로 새로운 사용자를 생성할 때에 기본적으로 사용할 홈디렉토리와 사용할 쉘종류, 종료일자, 기본소속그룹, 홈디렉토리에 복사될 초기화 파일들의 위치등을 정의하고 있는 파일입니다.
     GROUP=100
     HOME=/home
     INACTIVE=-1
     EXPIRE=   # EXPIRE="2010-10-30"
     SHELL=/bin/bash
     SKEL=/etc/skel
  b. /etc/login.defs
     MAIL_DIR  각 계정사용자들의 메일파일이 저장될 위치를 지정
     PASS_MAX_DAYS 계정사용자들이 패스워드를 변경하지 않고 동일한 패스워드를 지속적으로 사용할 수 있는 최대일자
     PASS_MIN_DAYS 패스워드 변경없이 사용할 수 있는 최소일자
     PASS_MIN_LEN 각 계정사용자들이 패스워드문자로 지정할 수 있는 최소바이트수(영문자수)
     PASS_WARN_AGE 패스워드 종료일자가 다가오기 몇일 정부터 패스워드 사용불가에 대한 안내메시지를 알려줄 것인가를 지정한 것이다.
     UID_MIN  일반사용자의 UID번호를 할당할 때에 자동 할당할 최소 UID번호를 지정
     UID_MAX  리눅스 서버에서 생성할 수 있는 UID의 최대값
     GID_MIN  새로 생성되는 그룹의 GID 시작번호
     GID_MAX  새로 생성되는 그룹에서 지정할 수 있는 최대 GID번호
     CREATE_HOME 새로 생성되는 계정사용자의 홈디렉토리를 생성할 것인가를 결정하는 값
  c. /etc/skel
     사용자를 생성할 때, 이 디렉토리의 모든 파일들이 사용자의 홈디렉토리로 복사된다.
2. 사용자 삭제
# userdel [-r] 사용자명
# userdel bible2
# userdel -r bible3
3. 사용자 정보변경
# usermod -d /usr/sspark5 sspark5 -> 홈디렉토리 변경
# usermod -e 2006-12-31 sspark5 -> 계정사용종료일 설정
# usermod -s /bin/csh sspark5 -> 기본사용쉘 변경
# usermod -u 508 sspark5 -> UID 변경
# usermod -l bible5 -p 12345678 sspark5 -> 아이디를 bible5로 패스워드를 12345678로 변경
4. 사용자를 일시적으로 로그인하지 못하게 하기
# grep test00 /etc/shadow
# passwd -l test00
test00:$1$tAeIn2Ca$mLxnIr.n4VO/4jqa85F/8/:13229:0:99999:7:::
# grep test00 /etc/shadow
test00:!!$1$tAeIn2Ca$mLxnIr.n4VO/4jqa85F/8/:13229:0:99999:7:::
# passwd -u test00
# grep test00 /etc/shadow
test00:$1$tAeIn2Ca$mLxnIr.n4VO/4jqa85F/8/:13229:0:99999:7:::
5. 모든 일반사용자들의 일시적인 서버접속 금지방법
# touch /etc/nologin -> 이 파일이 있으면 root 를 제외한 일반사용자는 로그인할 수 없다.

* passwd : 사용자 패스워드 관리
# passwd [-k] [-l] [-u [-f]] [-d] [-S] [사용자 ID]
# passwd widemail -> widemail 사용자 패스워드 변경
# passwd -S widemail -> widemail의 패스워드를 MD5로 암호화한다.
# passwd -l widemail -> widemail 사용자의 패스워드에 lock를 건다. widemail 사용자는 로그인할 수 없다.
# passwd -u widemail -> widemail 사용자에게 걸었던 lock을 해제한다.
# passwd -d widemail -> widemail 사용자자의 패스워드를 삭제한다. widemail 사용자는 어떤 패스워드로도 로그인이 가능하다.

* 그룹 관리
1. 그룹생성
# groupadd [-g GID] [-o] [-r] [-f] 새로생성할그룹명
# groupadd chongmu
# tail -1 /etc/group
chongmu:x:505:
# groupadd -g 1000 youngup
# tail -1 /etc/group
youngup:x:1000:
# groupadd -r sysadmin -> -r 옵션으로 시스템여역의 GID를 할당받는다.
# tail -1 /etc/group
sysadmin:x:11:
2. 그룹삭제
# groupdel sysadmin

* 로그인 메시지 관리
- /etc/issue  : 콘솔(console)접속 시도시(접속완료전)에 보여줄 메시지파일
- /etc/issue.net : 원격지에서 접속 시도시(접속완료전)에 보여줄 메시지파일
- /etc/motd  : ID, 패스워드로 인증을 받은 후 즉, 접속완료후에 보여줄 메시지파일

* 프롬프트 변경
쉘프롬프트의 모양을 결정하는 것은 프롬프트의 변수인 PS1이라는 변수에 값을 변경함으로써 가능합니다.
# echo $PS1
[\u@\h \W]\$
PS1쉘변수의 값으로 사용할 수 있는 기호들
\t 24시간제로 현재시간을 나타냄. 표시형식은 시:분:초, 즉 "HH:MM:SS"의 형식임
\T 12시간제로 현재시간을 "HH:MM:SS"형식으로 표시함
\@ 12시간제로 현재시간을 "오전/오후"형식으로 표시함
\d 현재 날짜를 나타냄. 표시형식은 "요일 월 일"의 문자로 표시됨
\s 현재 사용중인 쉘의 이름을 표시함(C쉘이면 /bin/csh, bash쉘이면 /bin/bash임)
\w 현재디렉토리의 전체 절대경로를 모두 표시함
\W 현재 디렉토리의 전체 절대경로명중 마지막 디렉토리명만을 표시함. 즉, 현재 디렉토리명만을 표시함.
\u 사용자명을 표시함
\h 서버의 호스트명을 표시함.(예: http://www.superuer.co.kr/에서 www부분)
\H 서버의 도메인명을 표시함.(예: http://www.superuser.co.kr/에서 superuser.co.kr부분)
\# 접속한 순간부터 사용한 명령어의 번호를 1번부터 차례대로 표시함. 즉, 명령어를 사용한 횟수를 표시하기 위함이 목적임.
\! 사용한 명령어의 history번호를 표시함
\$ 현재 사용자가 root(UID가 0이면)이면 #을 표시하고 아니면 $를 표시함
\\ "\"문자 자체를 표시함
\a ASCII 종소리 무자(07)
\E ASCII의 escape문자(033)
\n 개행문자(줄바꾸기)
\v 사용중인 bash의 버전
\V 사용중인 bash의 배포, 번전+패치수준으로 버전을 상세히 표시함.
\r Carrage return
\nnn 8진수 nnn에 해당하는 문자

* 시간 관리
1. clock : CMOS의 시간을 설정
-u : CMOS의 시각을 국제시각으로 조정한다.
-r : CMOS의 시각을 출력한다.
-w : 리눅스시스템 시각으로 CMOS시각을 조정한다.
-s : CMOS의 시각으로 리눅스시스템시각을 조정한다.
-a : CMOS의 시각으로 리눅스시스템시각으로 조정하고 다시 CMOS에 조정한다.
# clock -r -> CMOS 시간을 읽기
Thu 23 Mar 2006 12:56:56 AM KST  -0.918420 seconds
# clock -w -> 리눅스의 시스템시간으로 CMOS 시간 설정
# clock -s -> CMOS 시간으로 리눅스의 시스템시간을 설정
2. date
   - date 로 시간설정
# date MMDDhhmmYY -> 시간 설정
MM 월
DD 월 중 일
hh 시
mm 분
CC 연도의 처음 두 숫자(선택적)
YY 연도의 나중 두 숫자(선택적)
ss 초(선택적)
# date 020301012005
# date
Thu Feb 3 0:01:03 KST 2005
   - date 로 시간츨력
     a. 시간 필드:
       %H     시 (00..23)
       %I     시 (01..12)
       %k     시 ( 0..23)
       %l     시 ( 1..12)
       %M     분 (00..59)
       %p     로케일의 AM 또는 PM
       %r     시간, 12-시간제 (hh:mm:ss [AP]M)
       %s     1970-01-01 00:00:00 UTC (비표준 확장기능)로 부터 경과된 초
       %S     초 (00..61)
       %T     시간, 24-시간 (hh:mm:ss)
       %X     로케일에서 정의한 시간 표현(%H:%M:%S)
       %Z     시간대 (에, EDT), 시간대를 결정할 수 없는 때는 아무 값도 출력하지 않는다.
     b. 날짜 필드:
       %a     로케일의 약식 요일 이름 (Sun..Sat)
       %A     로케일의 완전한 요일 이름, 가변 길이 (Sunday..Saturday)
       %b     로케일의 약식 월 이름 (Jan..Dec)
       %B     로케일의 완전한 월 이름, 가변 길이 (January..December)
       %c     로케일의 날짜와 시간 (Sat Nov 04 12:02:33 EST 1989)
       %d     월 중 일 (01..31)
       %D     날짜 (mm/dd/yy)
       %h     %b 와 동일
       %j     연 중 일 (001..366)
       %m     월 (01..12)
       %U     연 중 주 번호, 일요일을 주의 첫번째 날로 생각 (00..53)
       %w     요일 번호 (0..6), 0 은 일요일
       %W     연 중 주 번호, 월요일을 주의 첫번째 날로 생각 (00..53)
       %x     로케일의 날짜 표현식 (mm/dd/yy)
       %y     연 중 일의 마지막 두 숫자 (00..99)
       %Y     연 (1970...)
3. rdate : 운격타임서버로부터 날짜시간정보 구하기
# rdate [-p] [-s] [-u] -[l] [타임서버]
# rdate -p time.bora.net
rdate: [time.bora.net]  Thu Mar 23 01:06:43 2006
# rdate -s time.bora.net
# rdate -s time.kriss.re.kr
# crontab -l | grep rdate
00 01 * * * /usr/bin/rdate -s time.bora.net && /sbin/clock -w

* 리눅스의 커널버전 및 배포판버전 확인
# uname -a
Linux mail.jpd.co.kr 2.6.15-1.1833_FC4 #1 Wed Mar 1 23:41:37 EST 2006 i686 i686 i386 GNU/Linux
# uname -r
2.6.15-1.1833_FC4
# cat /etc/redhat-release
Fedora Core release 4 (Stentz)
# cat /proc/version
Linux version 2.6.15-1.1833_FC4 (bhcompile@hs20-bc1-1.build.redhat.com) (gcc version 4.0.2 20051125 (Red Hat 4.0.2-8)) #1 Wed Mar 1 23:41:37 EST 2006
# cat /proc/sys/kernel/osrelease
2.6.15-1.1833_FC4

* USB 장치 사용
1. USB장치를 리눅스서버에 꼽아두십시오.
2. USB장치를 마운트할 마운트포인트를 생성한다.
# mkdir /media/usb
3. modprobe명령어로 ide-scsi모듈을 로드한다.
# modprobe ide-scsi
4. 리눅스에서 대부분 USB장치는 /dev/sda 또는 /dev/sdb 또는 /dev/sdc등의 장치명을 사용하는데 현재 리눅스서버에서 사용되고 있지 않은 장치명을 USB장치명으로 사용할수 있다.
   어떤 장치명에 할당되었는가를 확인하기 위하여 다음과 같이 "fdisk -l"을 실행해 본다.
# fdisk -l
Disk /dev/sdc: 32M, 32768000 bytes
2 heads ...
5. USB장치가 /dev/sdc장치를 사용하는 것을 확인했으므로, 해당 장치를 마운트한다.
# mount -t vfat /dev/sdc /media/usb
6. 사용이 끝나면 마운트를 해제한다.
# umount /media/usb

* rename 명령어
# ls
test1  test2  test3  test4  test5
# rename test test0 test?
  - test : 변경전 파일패턴
  - test0 : 변경후 파일패턴
  - test? : 적용대상파일들, 여기서 ?는 임의의 문자를 의미한다.
# ls
test01  test02  test03  test04  test05
# rename test0 test test??
# ls
test1  test2  test3  test4  test5

* find 명령어
사용형식: find [찾을 디렉토리경로] [찾기옵션] [찾은후 수행할 작업]
[찾을 디렉토리경로]
.   : 현재 디렉토리 이하를 검색대상으로 한다.
/   : 루트디렉토리(파일시스템전체)이하를 대상으로 한다.
~ID : 지정된ID의 홈대렉토리이하를 대상으로 한다.
[찾기옵션]
-empty  : 비어있는 파일을 찾는다.
-uid UID : 지정된 UID를 갖는 파일을 찾는다.
-gid GID : 지정된 GID를 갖는 파일을 찾는다.
-group 그룹명 : 지정된 group을 가진 파일을 찾는다.
-name  : 지정된 형식의 패턴을 가지는 파일을 찾는다.
-newer  : 지정된 파일 이후에 생성된 파일을 찾는다.
-perm  : 지정된 퍼미션을 가진 파일을 찾는다.
-used 일자수 : 최근 n일 이후에 변경된 파일을 찾는다.
-user  : 지정된 파일을 소유하고 있는 소유자의 모든 파일을 찾는다.
[찾은후 수행할 작업]
-exec "수행명령어" : 검색된 파일을 대상으로 "수행명령어"(쉘명령어)를 실행한다.
1. 특정 사용자의 ID소유로된 모든 파일 찾기
# find / -user test01 -print
2. 특정 사용자의 홈디렉토리 내에서  특정 파일 찾기
# find /home/test01 -name *.html -print
# find ~test01 -name *.html -print
3. 시스템내의 특정파일들을 검색하여 자동삭제하는 방법
# find ~test01 -name *.bak -exec rm -f {} \;
4. 지정한 소유자의 모든 파일을 찾아서 확인하기
# find / -user test01 -exec ls -l {} \;
5. 서버내의 불필요한 파일들 모두 정리(삭제)하기
# find / -name access_log -exec rm -f {} \;
6. 특정용량 이상되는 파일들 모두 찾기(용량단위로 파일검색)
# find /usr -size +10000k -exec ls -l {} \;
7. 특정 퍼미션을 가지고 있는 파일들 모두 찾기
# find / -perm 4755 -exec ls -l {} \;
8. 특정디렉토리내에 존재하는 모든 디렉토리 목록만 확인하기
# find ~test01 -type d -print
9. 서버내에 존재하는 모든 링크파일 검사하기
# find / -type l -exec ls -l {} \;
10. 쓰기 권한이 있는 파일 및 디렉토리 찾기
# find / -type f \( -perm -2 -o -perm -20 \) -exec ls -lg {} \;
# find / -type d \( -perm -2 -o -perm -20 \) -exec ls -ldg {} \;
11. 특정문자를 포함하는 파일찾기와 그 문자가 속한 행 찾기
# find ~test01 type f -exec egrep -i "doc" /dev/null {} \;

* 하드디스크 추가
1. 현재 사용중인 하드디스크 확인(추가전)
# cat /etc/fstab
LABEL=/                 /                       ext3    defaults        1 1
LABEL=/boot             /boot                   ext3    defaults        1 2
none                    /dev/pts                devpts  gid=5,mode=620  0 0
LABEL=/home             /home                   ext3    defaults        1 2
LABEL=/jms              /jms                    ext3    defaults        1 2
none                    /proc                   proc    defaults        0 0
none                    /dev/shm                tmpfs   defaults        0 0
LABEL=/tmp              /tmp                    ext3    defaults        1 2
/dev/sda3               swap                    swap    defaults        0 0
/dev/cdrom              /mnt/cdrom              udf,iso9660 noauto,owner,kudzu,ro 0 0
/dev/fd0                /mnt/floppy             auto    noauto,owner,kudzu 0 0
# df -k
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/sda2              5036316    884644   3895840  19% /
/dev/sda1               101089     14577     81293  16% /boot
/dev/sda6              1004024     66172    886848   7% /home
/dev/sda7              9345096   3285944   5584436  38% /jms
none                    127648         0    127648   0% /dev/shm
/dev/sda5              1004024     16436    936584   2% /tmp
# mount
/dev/sda2 on / type ext3 (rw)
none on /proc type proc (rw)
usbdevfs on /proc/bus/usb type usbdevfs (rw)
/dev/sda1 on /boot type ext3 (rw)
none on /dev/pts type devpts (rw,gid=5,mode=620)
/dev/sda6 on /home type ext3 (rw)
/dev/sda7 on /jms type ext3 (rw)
none on /dev/shm type tmpfs (rw)
/dev/sda5 on /tmp type ext3 (rw)
2. 추가할 HDD를 HDD확장슬롯에 장착한다.
3. 새로운 HDD를 장착한 후에 서버를 재부팅한다.
4. 재부팅이 끝나고 나면 dmesg란 명령어를 사용하여 장착된 SCSI HDD의 정보를 확인한다.
SCSI device sdb: 71687340 512-byte hdwr sectors (36704 MB)
5. SCSI ID를 확인했다면 이제 fdisk를 실행시켜 파티션을 설정해 준다.
6. 작성된 파티션을 사용하기 위해 파일시스템을 생성한다.
# mke2fs /dev/sdb1 -> for ext2 filesystem
# mke2fs /dev/sdb1 -j -L /data -> for ext3 filesystem
7. 준비된 파티션을 마운트를 시키기 위해 디렉토리를 작성하고 마운트를 시킨다.
# mkdir /data
# mount -t ext2 /dev/sdb1 /data -> for ext2 filesystem
# mount -t ext3 /dev/sdb1 /data -> for ext3 filesystem
8. 부팅시 자동마운트 되게 하기 위한 파일시스템테이블 수정
LABEL=/                 /                       ext3    defaults        1 1
LABEL=/boot             /boot                   ext3    defaults        1 2
none                    /dev/pts                devpts  gid=5,mode=620  0 0
LABEL=/home             /home                   ext3    defaults        1 2
LABEL=/jms              /jms                    ext3    defaults        1 2
none                    /proc                   proc    defaults        0 0
none                    /dev/shm                tmpfs   defaults        0 0
LABEL=/tmp              /tmp                    ext3    defaults        1 2
/dev/sda3               swap                    swap    defaults        0 0
/dev/cdrom              /mnt/cdrom              udf,iso9660 noauto,owner,kudzu,ro 0 0
/dev/fd0                /mnt/floppy             auto    noauto,owner,kudzu 0 0
LABEL=/data             /data                   ext3    defaults        1 2
9. 파일시스템 생성 최종확인
# df -k
Filesystem           1K-blocks      Used Available Use% Mounted on
/dev/sda2              5036316    884644   3895840  19% /
/dev/sda1               101089     14577     81293  16% /boot
/dev/sdb1             35278540     32828  33453664   1% /data
/dev/sda6              1004024     66172    886848   7% /home
/dev/sda7              9345096   3285944   5584436  38% /jms
none                    127648         0    127648   0% /dev/shm
/dev/sda5              1004024     16436    936584   2% /tmp

* 서버 메모리상태 점검하기
# free
             total       used       free     shared    buffers     cached
Mem:        255300     178908      76392          0      17696      20380
-/+ buffers/cache:     140832     114468
Swap:      1020116     230444     789672
- Mem행은 시스템의 물리적인 메모리에 대한 사용량을 각 필드로 표시한 것이다.
  total은 전체 메모리의 용량
  used는 현재 시스템에서 사용중인 메모리의 용량
  free는 현재 시스템에서 사용중이지 않은 메모리의 용량
  shared는 현재 시스템에서 공유한 메모리의 용량
  buffers는 현재 시스템에서 버퍼링(buffering)된 메모리의 용량
  cached는 현재 시스템에서 캐싱(caching)된 메모리의 용량
- -/+ buffers/cache 는 현재 캐시메모리에서 버퍼링된 사용량을 표시한다.
- Swap메모리는 스왑메모리의 용량이다.
  total은 시스템의 전체 Swap메모리의 용량
  used는 전체 스왑메모리에서 현재 사용중인 스왑메모리의 용량
  free는 전체 스왑메모리에서 사용되지 않고 남았는 메모리의 용량

* ping 응답 설정/해제
# echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all -> ping에 응답하지 않게 설정
# echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_all -> ping에 응답하도록 설정

* Quota 설정
quota와 관련하여 알아두셔야하는 것은 다음과 같습니다.
- 커널쿼타지원여부 : 커널(kernel)에서 해당 파일시스템의 quota지원이 되는가?
- quota   : 설정된 각 사용자(그룹)의 quota를 확인한다.
- edquota  : 각 사용자(그룹)의 디스크 quota를 설정한다.
- quotaon  : 시스템에서 quota를 가동한다.
- quotaoff  : 시스템에서 quota를 중지한다.
- quotacheck  : 시스템에서 quota 상황을 확인한다.
- repquota  : 사용자들의 quota설정내역을 확인한다.
1. quota 적용을 위한 피일시스템 수정사항
quota를 적용할 파티션의 파일시스템 마운트시에 usrquota라는 속성을 넣어서 마운트한다.
# grep usrquota /etc/fstab
LABEL=/home  /home  ext3 defaults,usrquota 1 2
2. 시스템커널에서 quota지원여부 확인하기
/var/log/messages파일과 dmesg명령어로 quota관련 내용을 확인한다.
# dmesg | grep quota
VFS: Disk quotas dquot_6.5.1
3. 특정 사용자의 quota설정 내역을 확인하는 방법
# quota -v dhan
Disk quotas for user dhan (uid 500):
Filesystem blocks quota limit grace files quota limit grace
/dev/sda6 23680 0 50000 1887 0 0
quota에서 "Soft Limit"이란 일반적인 용량제한범위를 의미하는 것으로 여기서 설정된 용량을
넘기게 되면 일단은 유예기간(grace period)내에서는 허용은 하지만 경고를 받게 됩니다.
quota에서 "Hard Limit"이란 절대적으로 넘을 수 없는 용량제한을 의미합니다.
사용자에게 허용된 용량의 10%정도를 추갈 저장할 수 있도록 설정할 수 있다.
quota에서 "Grace Period"란 유예기간으로서 해당 사용자의 용량이 "Soft Limit"을 넘기는
시점부터 적용되어 용량초과를 허용할 시간을 의미합니다.
4. 일반사용자의 자기자신의 quota설정내역 확인하는 방법
# quota -u dhan
Disk quotas for user dhan (uid 500):
Filesystem blocks quota limit grace files quota limit grace
/dev/sda6 23680 0 50000 1887 0 0
5. 특정 그룹의 디스크용량 설정내용 확인하기
# quota -g dhan
Disk quotas for group dhan (gid 500): none
6. 디스크사용량 제한 설정하는 edquota
# edquota -u dhan
설정한 후 quota를 적용할 파일시스템에서 aquota.user 파일을 생성한다.
# cd /home
# touch aquota.user
# chmod 640 aquota.user
7. quota가동과 중지
# quotaon -avug
-a : quota옵션이 들어가 있는 파일시스템은 자동으로 가동한다.
-v : quota가 가동된 각 파일시스템들의 메시지를 출력한다.
-u : 기본으로 적용되는 것으로 각 개별 사용자들의 quota설정을 읽어 적용한다.
-g : 각 그룹별 quota설정을 읽어 적용한다.
# quotaoff -aug
8. quota가동여부 및 상태 확인
# quotaon -a -p
9. quota가 설저오딘 파티션의 quota설정 및 용량제한 내역 확인
# repquota -av
-a : 보고할 파일시스템 대상을 지정하는 것으로 /etc/fstab에서 usrquota, grpquota가 설정된 모든 파일시스템을 대상으로 한다는 옵션이다.
-v : 사용량이 없는 사용자들의 보고도 포함하게 한다.
-g : 그룹quota에 대한 보고를 한다.
-u : 사용자의 qutoa에 대한 보고를 한다.(기본값)

* 동종 프로세스 모두 죽이기
# ps -ef | grep httpd | awk '{ print $2 }' | xargs kill -9
# killall httpd

* whowatc 유틸리티 : 현재 접속한 사용자 실시간 감시법

* idled 유틸리티 : 일정시간 미사용시 강제 로그아웃시키기

* 리눅스 커널에 이더넷카드 인식시키기
리눅스 커널에 이더넷카드를 인식하도록 하려면 다음과 같은 사항들을 확인해야합니다.
- 이더넷카드의 모듈이름
- 이더넷카드의 I/O(입출력)주소, 일반적으로 0x300을 사용함.
- 이더넷카드의 IRQ번호
- DMA를 사용하는 카드라면 DMA번호
- modprobe, insmod, lsmod, rmmod명령어 사용법
리눅스에서 커널모듈들은 모두 /lib/modules/<커널버전>/kernel 이라는 디렉토리에 위치하고 있다.
/lib/modules/<커널버전>/kernel/drivers/net이라는 디렉토리에는 네트워크 관련 커널모듈들이 있다.
레드햇 리눅스에서는 /boot/modules-info라는 파일내에 리눅스 커널모듈에 관한 정보를 저장하고 있다.
이렇게 확인 모듈을 커널에 올린다.
# modprobe 3c509 io=0x300 irq=13
# insmod 3c509 io=0x300 irq=13
대부분의 PCI카드는 io옵션과 irq옵션은 자동검색되므로 생략가능하다.
단 NE2000계열의 네트워크드라이브를 사용한다면 io옵션과 irq옵션을 지정해 주어야 한다.
그리고, io포트와 irq번호는 현재 사용중이 아닌 값으로 지정한다.
사용중인 io포트번호 확인
# cat /proc/ioports
사용중이 irq번호 확인
# cat /proc/interrupts
서버 재부팅후에 지속적으로 적용하기 위해서는 /etc/modules.conf파일에 (또는 /etc/modprobe.conf 파일)
관련 모듈 설정을 입력해 주어야 한다.
# cat /etc/modules.conf
alias eth0 e100
alias scsi_hostadapter aic7xxx
alias usb-controller usb-uhci

* 하드디스크 배드블록 검사
# badblocks -v /dev/sdb5
# badblocks -v /dev/sdb5 -o badlocks.txt
# badblocks /dev/fd0H1440 1440
- 디스크의 배드블록 마크하기
# e2fsck -cv /dev/sdb5

* 하드디스크 속도점검
- 버퍼링되어 있지않은 데이터의 디스크 ACCESS 속도 검사
# hdparm -t /dev/hda
- 버퍼링되어 있는 데이터의 디스크 ACCESS속도 검사
# hdparm -T /dev/hda
- CD-ROM 드라이브 읽는속도 검사
# hdparm -t /dev/cdrom

* fdisk : 디스크 파티션 작업 프로그램
현재 모든 디스크의 파티션설정현황 파악하기
# fdisk -l
특정 파티션을 대상으로 한 fdisk 시작과 종료
# fdisk /dev/sdb

The number of cylinders for this disk is set to 4462.
There is nothing wrong with that, but this is larger than 1024,
and could in certain setups cause problems with:
1) software that runs at boot time (e.g., old versions of LILO)
2) booting and partitioning software from other OSs
   (e.g., DOS FDISK, OS/2 FDISK)

Command (m for help): m
Command action
   a   toggle a bootable flag   -> 부팅파티션을 설정할 수 있음.
   b   edit bsd disklabel   -> BSD로 디스크라벨 설정
   c   toggle the dos compatibility flag -> DOS호환가능한 플래그 설정
   d   delete a partition   -> 파티션을 삭제할 수 있음.
   l   list known partition types  -> 설저가능한 파티션 타입을 보여줌.
   m   print this menu    -> 파티션 설정도움말을 보여줌.
   n   add a new partition   -> 새로운 파티션을 생성할 수 있음.
   o   create a new empty DOS partition table -> 새로운 도스파티션 테이블을 생성함.
   p   print the partition table  -> 현재 설정된 파티션정보를 보여줌.
   q   quit without saving changes  -> 설정된 정보를 저장하지 않고 fdisk를 종료.
   s   create a new empty Sun disklabel  -> SUN 디스크라벨을 생성함.
   t   change a partition's system id  -> 파티션 타입을 변경할 수 있음.
   u   change display/entry units  -> 유닛(UNITS)정보를 열람하거나 변경할 수 있음.
   v   verify the partition table  -> 지정된 파티션을 검사함.
   w   write table to disk and exit  -> 현재까지 설정한 파티션정보를 저장하고 fdisk 종료.
   x   extra functionality (experts only) -> 파티션 전문가 모드로 들어감. 실린터수나 헤드수 그리고 트랙당 섹터수를 변경할 수 있음.

Command (m for help): q

* 리눅스 파일시스템 포맷(생성)하기
- mke2fs로 파일시스템을 생성하면 다음과 같은 세부항목들이 파일시스템내에 생성됩니다.
1. Block size 크기
2. Fragment size 크기
3. indoe의 수
4. 수퍼유저가 사용할 block의 수(전체 block의 5%를 차지함)
5. 첫 번째 데이터블록
6. Block group의 수
7. 그룹당 블록수
8. 그룹당 fragment의 수
9. 그룹당 inode의 수
# mke2fs [-c|-l 파일명] [-b 블로그기] [-f 프레그먼트ㅡ기] [-i 노드당바이트수] [-m 예약블록퍼센트] [-q] [-v] [-F] [-S] 장치명 [블록갯수]
# mke2fs -t ext2 /dev/hdb1 -> /dev/hdb1을 ext2 타입의 파일시스템으로 생성
# mke2fs -j /dv/hdb1  -> /dev/hdb1을 ext3 타입의 파일시스템으로 생성
# mke2fs -j -b 4096 /dev/hdb1 -> /dev/hdb1에 대해서 ext3타입의 파일시스템을 생성하면서 블록의 크기를 4069byte로 생성
# mke2fs -c /dev/hdb1  -> /dev/hdb1에 대한 배드블록을 점검한 후에 ext2파일시스템을 생성
# mke2fs -i 4096 /dev/hdb1 -> 아이노드 당 바이트수를 지정하여 파일시스템을 생성. -i옵션으로 주어지는 값은 최소 1024이상이다. 기본값은 4096
# mke2fs -j -m 20 /dev/hdb1 -> ext3파일시스템(-j)을 생성하면서 수퍼유저(root)의 예비블록으로 20%를 남겨둔다.
# mke2fs /dev/sdb1 -j -L /data
- 디스크 추가 장착시 작업 순서
1. 디스크 장착
2. fdisk로 파티션 생성
3. mkfs or mk2fs로 파일시스템 생성
4. mount 작업
5. 부팅시 자동마운트를 위하여 /etc/fstab에 등록
- mkfs로 리눅스 파일시스템 생성
# mkfs [-V] [-t 파일시스템타입] [파일시스템옵션] 장치이름 [블록]
옵션설명
-V : 자세한 정보를 보여준다.
-t : 생성할 파일시스템타입(ext2, ext3)
파일시스템옵션
-c : 파일시스테을 생성하기 전에 배드블록을 검사
-l 파일명 : 지정된 파일명으로부터 배드블록목록을 읽는다.
-v : 작업상태와 결과를 자세히 보여준다.
장치이름 : 자치명
# mkfs -t ext3 /dev/hdb1 -> ext3 타입의 파일시스템 생성
  => # mkfs.ext3 /dev/hdb1
# mkfs -t ext2 /dev/hdb1 -> ext2 타입의 파일시스템 생성
  => # mkfs.ext2 /dev/hdb1
# mkfs -V -t ext3 -j -L /data /dev/sdb1

* mount : 파일시스템 마운트하기
# mount -a [fnrvw] [-t 파일시스템타입]
# mount [-fnrvw] [-o 옵션[,...]] 장치 디렉토리
# mount [-fnrvw] [-t 파일시스템타입] [-o 옵션[,...]] 장치 디렉토리
# mount -t iso9660 /dev/cdrom /mnt/cdrom
# mount -r /dev/hdb1 /backup -> /dev/hdb1 파티션을 /backup디렉토리로 마운트를 하면서 -r옵션을 사용히여 쓰기 금지옵션을 사용하여 마운트한다. /backup디렉토리는 읽기만 가능하다.
# mount -w /dev/hdb1 /backup -> /dev/hdb1 파티션을 /backup디렉토리로 마운트하면서 -w옵션을 사용하여 쓰기가 가능하도록 마운트한다.
# mount -t ext3 /dev/hdb1 /backup
# mount -t vfat /dev/fd0 /mnt/floppy
# mount -t nfs 192.168.0.200:/web_data /web_data -> 마운트가 되면 192.168.0.200서버의 /web_data디렉토리를 현재 서버의 /web_data디렉토리처럼 사횽할 수 있다.
# mount -t nfs 192.168.0.201:/db_data /db_data

* /etc/fstab : 파일시스템 자동마운트 정보파일
# cat /etc/fstab
LABEL=/                 /                       ext3    defaults        1 1
LABEL=/boot             /boot                   ext3    defaults        1 2
LABEL=/data             /data                   ext3    defaults        1 2
none                    /dev/pts                devpts  gid=5,mode=620  0 0
LABEL=/home             /home                   ext3    defaults        1 2
LABEL=/jms              /jms                    ext3    defaults        1 2
none                    /proc                   proc    defaults        0 0
none                    /dev/shm                tmpfs   defaults        0 0
LABEL=/tmp              /tmp                    ext3    defaults        1 2
/dev/sda3               swap                    swap    defaults        0 0
/dev/cdrom              /mnt/cdrom              udf,iso9660 noauto,owner,kudzu,ro 0 0
/dev/fd0                /mnt/floppy             auto    noauto,owner,kudzu 0 0
- /etc/fstab 파일 구조
[파일시스템장치명] [마운트포인트] [파일시스템] [옵션] [dum관련설정] [파일점검옵션]
[파일시스템장치명] : 파일시스템의 장치명. 만약 레이블(LABEL)이 설정되어 있다면 장치명 대신 에이블명으로 지정할 수도 있다.
[마운트포인트]     : 파일시스템이 마운트될 위치.
[파일시스템]       : 파일시스템의 종류
    ext  초기 리눅스에서 사동되었던 파일시스템
    ext2 현재 많이 사용하고 있는 파일시스템
    ext3 저널링파일시스템으로서 ext2에 비해 파일시스템 복구기능과 보안부분을 크게 향상시킨 파일시스템
    iso9660 CD-ROM의 표준 파일시스템
    nfs  Network File System으로서 원격서버를 마운트할 때 사용.
    swap 스왑파일시스템
    ufs  UNIX FileSystem으로서 UNIX SYSTEM 5계역ㄹ에서는 표준파일시스템이다.
    vfat 윈도우 95나 98, 그리고, NT를 지원하기 위한 파일시스템
    msdos MS-DOS파티션을 사용하기 위한 파일시스템
    hpfs HPFS 파일시스템
    ntfs 윈도우NT나 200의 NTFS 파일시스템
    sysv 유닉스시스템 V를 지원하기 위한 파일시스템
    hfs  Mac 컴퓨터의 hfs파일시스템
    ramdisk RAM 디스크를 지원하는 파일시스템
[옵션]             : 파일시스템을 용도에 맞게 사용하기 위한 파일시스템 속성을 설정하는 옵션이다.
    defaults rw, nouser, auto, exec, suid 속성을 모두 가지는 속성임. 가장 일반적인 파일시스템에서 사용하는 속성임.
    auto 부팅시 자동마운트됨
    exec 실행파일이 실행되는 것을 허용하는 파일시스템
    suid SetUID와 SetGID의 사용을 허용하는 파일시스템
    ro  읽기전용 파일시스템으로만 사용됨(Read Only)
    rw  일고 쓰기 파일시스템으로 사용됨(Read, Write)
    user 일반 계정사용자들도 마운트를 할 수 있는 파일시스템.
    nouser 일반 계정사용자들은 마운트 할 수 없는 파일시스템. root만 mount할 수 있다.
    noauto 부팅시 자동마운트되지 않게 한다.
    noexec 실행파일을 실행되지 못하게 하는 파일시스템
    nosuid SetUID와 SetGID의 사용을 허용하지 않는 파일시스템
    usrquota 개별 계정사용자의 quota설정이 가능한 파일시스템
    grpqutoa 그룹별 quota설정이 가능한 파일시스템
[dum관련설정]      : 0또는 1을 가질 수 있다. 1은 데이터백업등을 위해 dump가 가능한 파일시스템이며, 0은 dump명령으로 덤프되지 않는 파일시스템이다.
[파일점검옵션]     : 0또는 1, 그리고 2가 올 수 있다. 0은 부팅시 실행되는 fsck가 실행되지 않는 설정이며, 1은 루트파일시스템을 의미하며, 2는 루트파일시스템이외의 파일시스템을 의미한다. 여기서 설정된 파일시스템의 순서를 기준으로 부팅시 실행되는 fsck의 순서가 결정된다.

* tune2fs : 파일시스템 튜팅 프로그램
# tune2fs [-l] [-c 최대마운트횟수] [-e 에러발생시반응] [-i 각점검간의간격] [-m 예약블록 퍼센트] [-r 예약블록갯수] [-u 사용자] [-g 그룹] 장치명
# tune2fs -l /dev/sdb1 == # dumpe2fs -h /dev/sdb1
tune2fs 1.38 (30-Jun-2005)
Filesystem volume name:   /dtc
Last mounted on:          <not available>
Filesystem UUID:          cfa85fdd-ba5b-461b-830b-b405810abcc4
Filesystem magic number:  0xEF53
Filesystem revision #:    1 (dynamic)
Filesystem features:      has_journal ext_attr filetype needs_recovery sparse_super
Default mount options:    (none)
Filesystem state:         clean
Errors behavior:          Continue
Filesystem OS type:       Linux
Inode count:              4480448
Block count:              8960764
Reserved block count:     448038
Free blocks:              7826381
Free inodes:              4478614
First block:              0
Block size:               4096
Fragment size:            4096
Blocks per group:         32768
Fragments per group:      32768
Inodes per group:         16352
Inode blocks per group:   511
Last mount time:          Tue Mar  7 18:34:27 2006
Last write time:          Tue Mar  7 18:34:27 2006
Mount count:              6
Maximum mount count:      35
Last checked:             Thu Feb 23 18:50:02 2006
Check interval:           15552000 (6 months)
Next check after:         Tue Aug 22 18:50:02 2006
Reserved blocks uid:      0 (user root)
Reserved blocks gid:      0 (group root)
First inode:              11
Inode size:               128
Journal inode:            8
Journal backup:           inode blocks
# tune2fs -c 100 /dev/hdb1 -> /dev/hdb1 파일시스템의 최대 마운트횟수를 100으로 설정
# tune2fs -r 1000000 /dev/hdb1 -> /dev/hdb1 파일시스템의 예약블록갯수가 1000000으로 변경
# tune2fs -g wheel /dev/hdb1 -> /dev/hdb1 예약블록을 wheel그룹이 사용가능하도록 설정
# tune2fs -g 10 /dev/hdb1 -> /dev/hdb1 예약블록을 GID가 10인 그룹이 사용가능하도록 설정
# tune2fs -u dhan /dev/hdb1 -> /dev/hdb1 파일시스템을 dhan이라는 사용자도 예약블록을 사용할 수 있도록 설정
# tune2fs -u 500 /dev/hdb1 -> /dev/hdb1 파일시스템을 UID가 500인 사용자도 예약블록을 사용할 수 있도록 설정
# tune2fs -m 20 /dev/hdb1 -> /dev/hdb1 파일시스템의 예약블록의 용량을 20%로 설정

* Swap 용량 늘이기
1. swap파일시스템을 이용하기
# free
             total       used       free     shared    buffers     cached
Mem:        255300     231464      23836          0      22816      34928
-/+ buffers/cache:     173720      81580
Swap:      1020116     199116     821000
# swapon -s
Filename                        Type            Size    Used    Priority
/dev/sda3                       partition       1020116 199116  -1
# umount /data
# fdisk /dev/sdb -> 기존에 사용하던 파티션중 하나를 swap 파티션으로 변경한다.

The number of cylinders for this disk is set to 4462.
There is nothing wrong with that, but this is larger than 1024,
and could in certain setups cause problems with:
1) software that runs at boot time (e.g., old versions of LILO)
2) booting and partitioning software from other OSs
   (e.g., DOS FDISK, OS/2 FDISK)

Command (m for help): p

Disk /dev/sdb: 36.7 GB, 36703918080 bytes
255 heads, 63 sectors/track, 4462 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

   Device Boot    Start       End    Blocks   Id  System
/dev/sdb1   *         1      4462  35840983+  83  Linux

Command (m for help): t
Selected partition 1
Hex code (type L to list codes): 82
Changed system type of partition 1 to 82 (Linux swap)

Command (m for help): p

Disk /dev/sdb: 36.7 GB, 36703918080 bytes
255 heads, 63 sectors/track, 4462 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes

   Device Boot    Start       End    Blocks   Id  System
/dev/sdb1   *         1      4462  35840983+  82  Linux swap

Command (m for help): w
The partition table has been altered!

Calling ioctl() to re-read partition table.
Syncing disks.
# mkswap /dev/sdb1 -> /dev/sdb1에 swap 파일시스템 생성
Setting up swapspace version 1, size = 36701159 kB
# swapon /dev/sdb1 -> /dev/sdb1 swap 파일시스템을 활성화한다.
# free
             total       used       free     shared    buffers     cached
Mem:        255300     249548       5752          0      23036      35388
-/+ buffers/cache:     191124      64176
Swap:     36861088     199116   36661972
# swapon -s
Filename                        Type            Size    Used    Priority
/dev/sda3                       partition       1020116 199116  -1
/dev/sdb1                       partition       35840972        0       -2
# cat /etc/fstab
LABEL=/                 /                       ext3    defaults        1 1
LABEL=/boot             /boot                   ext3    defaults        1 2
LABEL=/data             /data                   ext3    defaults        1 2
none                    /dev/pts                devpts  gid=5,mode=620  0 0
LABEL=/home             /home                   ext3    defaults        1 2
LABEL=/jms              /jms                    ext3    defaults        1 2
none                    /proc                   proc    defaults        0 0
none                    /dev/shm                tmpfs   defaults        0 0
LABEL=/tmp              /tmp                    ext3    defaults        1 2
/dev/sda3               swap                    swap    defaults        0 0
/dev/sdb1               swap                    swap    defaults        0 0
/dev/cdrom              /mnt/cdrom              udf,iso9660 noauto,owner,kudzu,ro 0 0
/dev/fd0                /mnt/floppy             auto    noauto,owner,kudzu 0 0
# swapoff /dev/sdb1 -> /dev/sdb1 swap 파일시스템 해제.
# free
             total       used       free     shared    buffers     cached
Mem:        255300     234344      20956          0      21928      37092
-/+ buffers/cache:     175324      79976
Swap:      1020116     199116     821000
# swapon -s
Filename                        Type            Size    Used    Priority
/dev/sda3                       partition       1020116 199116  -1
2. swap파일을 이용하기
# dd if=/dev/zero of=swapfile bs=1024 count=1000 -> swap으로 사용할 파일 생성
1000+0 records in
1000+0 records out
# ls -l swapfile
-rw-r--r--    1 root     root      1024000 Mar 24 00:22 swapfile
# mkswap swapfile -> swap 파일에 swap 파일시스템생성
Setting up swapspace version 1, size = 1019 kB
# swapon swapfile -> swap 활성화
# free
             total       used       free     shared    buffers     cached
Mem:        255300     180912      74388          0      19836      23132
-/+ buffers/cache:     137944     117356
Swap:      1021108     199140     821968
# swapon -s
Filename                        Type            Size    Used    Priority
/dev/sda3                       partition       1020116 199140  -1
/data/swapfile                  file            992     0       -3
# swapoff swapfile
# free
             total       used       free     shared    buffers     cached
Mem:        255300     180968      74332          0      19932      23148
-/+ buffers/cache:     137888     117412
Swap:      1020116     199140     820976
# swapon -s
Filename                        Type            Size    Used    Priority
/dev/sda3                       partition       1020116 199140  -1

* finger : 로컬/원격 서버의 사용자 계정정보 확인
# finger widemail -> 로컬 계정사용자(widemail)의 계정정보 출력
Login: widemail                         Name: (null)
Directory: /home/widemail               Shell: /bin/bash
On since Thu Mar 23 14:55 (KST) on pts/1 from 192.168.0.11
   8 hours 46 minutes idle
On since Thu Mar 23 22:12 (KST) on pts/2 from 192.168.0.12
No mail.
No Plan.
# finger @192.168.0.3 -> 192.168.0.3에 접속해 있는 계정사용자 모두를 확인
# findger widemail@192.168.0.2 -> 192.168.0.2에 접속해 있는 widemail 게정정보 출력

* 파일 복수
# cp
-a : 복사대상 원본파일의  속성과 링크정보등을 그대로 유지하면서 복사하는 옵션 (= -dpR)
-b : 복사하고자 하는 파일이 동일한 이름으로 이미 그 위치에 존재하고 있을 경우, 덮어쓰기 또는 원본을 지우고 복사할 경우에 원본파일의 복사본을 만든다.
-f : 복사대상파일이 이미 그 위치에 존재한다면 파일을 지우고 복사한다.
-i : 복사대상파일이 이미 그 위치에 존재한다면 덮어쓸 것인가 또는 복사하지 않을 것인가를 사용자에게 확인하는 절차를 가진다.
-P : 복사대상이 되는 원본파일이 디렉토리경로와 함께 지정되었을 경우에 지정된 디렉토리경로를 그대로 복사한다.
-u : 복사되는 원본파일의 이름과 동일한 파일이 대상위치에 존재할 경우에 온본파일과 변경날짜를 비교하여 최신파일일 경우에 복사하지 않는다.
# install : 관리자 전용 복사명령어
# install [옵션] [-s] [--strip] 원본파일 복사파일
# install [옵션] [-s] [--strip] 원본파일.. 복사파일
# install [옵션] [-d,--directory] 디렉토리
-c : 전총 유닉스 버전과 호환을 위한 옵션
-d,--directory : 지정한 파일이 복사될 경로를 지정하는 옵션으로서 만약 지정한 경로명의 디렉토리가 존재하지 않는다면 직접 만들어서 복사를 한다.
-g,--group : 지정한 파일 복사시에 이 옵션으로 지정한 그룹명을 복사되는 파일의 소유그룹으로 지정하여 복사한다.
-m,--mode : 지정한 파일 복사시에 이 옵션으로 지정한 퍼미션을 복사되는 파일의 퍼미션으로 한다.
-o,--owner : 지정한 파일복사시에 이 옵션으로 지정한 소유자를 복사되는 파일의 소유자로 한다.
-s,--strip : 지정한 파일복사시에 이 옵션이 사용되면 strip명령을 수행한다.

* 디스크 사용량 점검
- du : 특정 디렉토리의 용랴응ㄹ 확인
# du -sh /etc -> /etc 전체 파일 및 디렉토리 용량의 합을 표시
- df : 파일시스템별 디스크사용량 점검
# df -k  -> 디스크사용량을 파티션별로 확인할 때 용량을 Kbyte 단위로 표시
# df -m  -> 디스크사용량을 파티션별로 확인할 때 용량을 Mbyte 단위로 표시
# df -h  -> 디스크사용량을 파티션별로 확인할 때 가장 적당한 용량단위로 표시
# df -a  -> 디스크용량 확인시에 모든 파일시스템을 대상으로 점검
# df -i  -> 파티션별 디스크용량 점검시에 inode사용정보를 표시
# df -T  -> 디스크사용량을 출력할 때에 파일시스템의 종류와 함께 표시
# df -t ext3 -> 특정 파일시스템의 종류만을 대상으로 디스크사용량 조사
# df -x ext3 -> 특정 파일시스템의 종류를 제외한 디스크사용량 조사

* ls
-R : 지정한 디렉토리이하에 있는 하부디렉토리와 파일들을 모두 포함한다.
-r : 출력결과를 정열할 때에서 내림차순으로 정렬한다.
-S : 파일사이즈가 가장 큰 것부터 순서대로 나열하게 된다.
-l : 파일들을 나열할 때에 자세한 출력을 한다.
-a : 경로안의 ㅁ든 파일을 나열한다. '.'으로 시작하는 파일들도 포함한다.
-A : 출력결과의 파일들에서 .과 ..은 제외한다.
-h : 파일사이즈를 용량단위(Mb, Gb)를 붙여서 출력한다.
# ls -l -> 찰일정보 자세히 표시
# ls -lr -> 파일명을 기준으로 내림차순 정렬하기
# ls -al -> 특정 디렉토리의 모든 파일 확인하기
# ls -m -> 콤마로 파일들을 구분하기
# ls -lF -> 파일의 끝부분에 파일형태를 표시하는 특수문자 표시하기
  일반적인 파일 : 아무런 표시도 하지 않는다.
  실행파일 : "*"를 붙여서 표시
  디렉토리 : "/"를 붙여서 표시
  심볼릭링크 : "@"를 붙여서 표시
  FIFO파일 : "|"를 붙여서 표시
  소켓파일 : "="를 붙여서 표시
# ls -aAl -> 현재디렉토와 상위디렉토리를 제외하고 표시
# ls -n -> 파일의 UID, GID 표시
# ls -lG -> 소유그룹정보 제외하기
# ls -1 -> 한 줄에 한 파일씩만 표시
# ls -lB -> 파일의 끝에 '~'가 붙은 파일(백업파일)은 출력제외하기
# ls -w 60 -> 가로길이를 지정하여 표시
# ls -l -I b* -> 특정패턴의 파일은 나열에서 제외하기
# ls -lR -> 서브디렉토리내의 모든 파일 함께 표시
# ls -lS -> 파일의 용량별로 내림차순 정렬하여 표시
# ls -lSr -> 파일의 용량별로 오름차순 정렬하여 표시
# ls -lX -> 파일확장자순으로 정렬하여 표시
# ls -lU -> 디스크저장 순서대로 표시
# ls -lL -> 심볼릭 링크파일을 일반 파일형태로 표시
# ls -lc -> 최근 변경시간을 기준으로 정령하여 표시
# ls -lt -> 파일의 시간순서대로 표시
# ls -lu -> 파일의 사용시간 순서대로 표시 (파일을 언제 접근했는지 표시)
# ls --fule-time -> 시간표시를 자세히 표시
# ls -li -> 파일리스트 맨 앞에 파일 색인번호를 표시
# ls -x -> 가로로 나열하여 표시
# ls -ls -> 파일의 크기를 KB단위로 표시
# ls -alRSh /backup -> 특정디렉토리 이하의 모든 파일을 대상으로 용량별로 정렬하여 표시

* 문자열 검색 및 편집
- grep에서 사용되는 검색문자패턴 정규표현식
  . : 아무문자나 한문자를 의미함.
  * : 어떤문자열이나 문자길에 무관한 문자열을 의미함.
  ^ : 행의 시작 위치를 의미함.
  $ : 행의 미지막 위치를 의미함.
  [] : 한문자 길이의 패턴리스트
  [^] : 지정된 문자들 이외의 문자들을 의히마(^는 여집합을 의미함)
  \ : 위에 오는 문자를 문자 그대로 인식(특수문자를 그대로 표현할 때 사용함)
  /< : 단어의 시작 위치를 의미함
  /> : 단어의 마지막 위치를 의미함
# grep [옵션] 검색할문자표현식 [파일..]
# grep [옵션] [-e 검색할문자표현식 | -f 파일] [파일..]
# 지정된 파일내에서 특정문자 찾기
# grep apache README -> README 파일에서 apache가 있는 행을 출력
# 지정된 파일내에서 특정문자를 포함한 행의 개수 출력
# grep -c apache README -> README에서 apache라는 문자열있는 행의 개수 출력
# 지정된 파일내에서 특정문자가 존재하지 않는 행만 검색
# grep -v apache README -> README에서 apache라는 문자열이 없는 행을 출력
# 지정된 파일 내에서 대소분자 구분없이 특정문자 검색하기
# grep -i apache README
# 특정 디렉토리내에서 디렉토리리스트만 확인하기
# ls -l /etc/ | grep "^d"
# 특정 디렉토리내에서 파일리스트만 보기
# ls -l /etc/ | grep -v "^d"

* touch
# 특정 파일의 날짜시간정보 변경
# touch MMDDhhmm[[CC]YY][.ss]
$ ls -l a
-rw-r--r--  1 widemail jms 21 Jan  1 10:10 a
$ touch -t 03241201 a
$ ls -l a
-rw-r--r--  1 widemail jms 21 Mar 24 12:01 a
# 특정파일의 날짜시간과 동일한 날짜시간을 다른 파일에 적용하기
$ ls -l a b
-rw-r--r--  1 widemail jms 21 Mar 24 12:01 a
-rw-r--r--  1 widemail jms  0 Jan  1 01:01 b
$ touch -r a b -> a파일의 날짜시간정보를 읽어서 b에 동일하게 적요
$ ls -l a b
-rw-r--r--  1 widemail jms 21 Mar 24 12:01 a
-rw-r--r--  1 widemail jms  0 Mar 24 12:01 b

* tar
# tar
-c : tar파일을 생성할 때(여러개의 파일을 하나의 파일로 묶을 때) 반드시 사용
-d : tar파일과 해당 파일시스템간의 차이점을 확인 하고자 할 때 사용
-r : tar파일에 다른 파일들을 추가하고자 할 경우에 사용
-t : tar파일의 내용을 확인하고자 할 때 반드시 사용
-f : tar파일을 사용할 때에는 반드시 사용
-p : tar파일을 생성할(풀) 때 원본 파일퍼미션을 그대로 유지함.
-v : 묶을 때나 풀 때 그 과정을 자세하게 보려고 할 때 사용
-Z : compress로 압축파일을 사용할 때 압축이나 해제까지 한번에 할 때 사용
-z : gzip(gunzip)과 관련하여 압축이나 해제를 한꺼번에 하려고 할 때 사용
-j : bzip2(bunip2)과 관련하여 압축하거나 해제하는 옵션으로 사용
# tarㄹ 파일 묶는 가장 기본적인 사용법
# tar cvf dir1.tar dir1
# tar로 묶여진 파일내의 파일리스트 확인하기
# tar tvf dir1.tar
# tar로 묶여지 파일 풀기
# tar xvf dir1.tar
# tar로 파일묶기와 압축하기를 동시에 하기
# tar cvfz dir2.tar.gz dir2
# tar로 묶고 압축된 파일 풀기
# tar xvfz dir2.tar.gz
# tar로 원본파일의 퍼미션을 그대로 유지한채로 묶고 압축하기
# tar cvfpz dir2.tar.g dir2
# tar로 워논파일 퍼미션을 유지한 채로 묶고 압축한 파일 풀기
# tar xvfpz dir2.tar.gz
# tar로 압축시에 용량단위로 나누어 압축하기 그리고 풀기
# tar cvfpz - /home/dhan | split -b 100m - widemail.tar.gz
# ls -l widemail.tar.g*
widemail.tar.gzaa widemail.tar.gzab widemail.tar.gzac
# cat home.tar.gz* | tar xvfpz -
# tar와 bzip2로 압축하고 tar와 bunzip2로 압축풀기
# tar cvfpj etc.tar.bz2 /etc
# tar xvfpj etc.tar.bz2


***********************************************************
* 로그 관리
***********************************************************

* 기본로그파일의 종류와 이해
리눅스 서버에서는 발생되는 기본적인 로그에는 ㅡ게 두가지가 있습니다. 첩선째 커널로그로서 kernel messages 라고하는 것으로 klogd라는 데몬에 의해 생성됩니다. 두번째 syslogd데몬에 의해 생성되는 로그입니다.
리눅스에서는 /var/log디렉토리에서 시스템의 모든 로그를 기록 및 관리하고 있습니다.
시스템의 /etc/syslog.conf파일에서 시스템 로그파일들의 위치를 지정하고 있습니다.
/var/log/boot.log : 리눅스가 부팅이 될 때 출력되는 모든 메시지를 기록하고 있다.
/var/log/cron: 시스템의 정기적이 작업에 대한 로그, 즉 시스템 cron작업에 대한 기록을 하고 있는 파일이다.
/var/log/messages : 리눅스 시스템의 가장 기본적인 시스템로그파일로서 시스템 운영에 대한 전반적인 메시지를 저장하고 있다.
/var/log/secure : 주로 사용자들의 원격로그인 정보를 기록하고 있는 로그파일ㄹ서 서버보안에 아주 민감하고 중용한 파일이다.
/var/log/xferlo : 리눅스 시스템의 FTP 로그파일로서 proftpd 또는 vsftpd 데몬들의 서비스내역을 기록하는 파일이다.

* /etc/syslog.conf 파일의 설정포맷
$ cat /etc/syslog.conf
# Log all kernel messages to the console.
# Logging much else clutters up the screen.
#kern.*                                                 /dev/console  -> kernel에 관련된 로그를 /dev/console에 출력하라.

# Log anything (except mail) of level info or higher.
# Don't log private authentication messages!
*.info;mail.none;authpriv.none;cron.none                /var/log/messages -> mail, 접속시의 인증 등에 대한 로그를 /var/messages에 기록하라.

# The authpriv file has restricted access.
authpriv.*                                              /var/log/secure  -> xinetd(tcp_wrapper)에 관련된 로그들을 /var/log/secure에 기록하라.

# Log all the mail messages in one place.
mail.*                                                  /var/log/maillog -> 메일에 관련된 로그를 /var/log/mail에 기록하라.


# Log cron stuff
cron.*                                                  /var/log/cron  -> 시스테므로(crond)에 관련된 로그를 /var/log/cron에 기록하라.

# Everybody gets emergency messages
*.emerg                                                 *

# Save news errors of level crit and higher in a special file.
uucp,news.crit                                          /var/log/spooler

# Save boot messages also to boot.log
local7.*                                                /var/log/boot.log -> 시스템이 부팅될 때의 로그메시지로서 /var/log/boot.log 파일에 기록하라.

/etc/syslog.conf파일의 각 행들은 다음과 같은 포맷으로 정의되어 있다.
facility.priority logfile-location
facility 서비스(데몬)에 대하여 priority의 경우에 해당하는 상황이 발생하였을 때 logfile-location 파일에 그 기록을 남겨라.
- facility : 서비스이름(메시지의 종류)
  auth : 로그인과 같이 사용자 인증에 관한 메시지
  autpriv : 보안 및 승인에 간한 메시지
  cron : crond데몬과 atd데몬에 의해 발생되는 메시지
  daemon : telnet, ftp등과 가은 데몬에 의한 메시지
  kern : kernel에 의한 메시지로서 커널메시지라고함.
  lpr : 프린터데몬인 lpd에 의해 발생되는 메시지
  mail : sendmail또는 pop또는 qmail등의 메일에 의해 발생되는 메시지
  news : innd등과 같은 뉴스시스템에 의해 발생되는 메시지
  uucp : uucp 에 의한 시스테에 의한 메시지
- priority : 메시지의 우선순위를 의미함.
  emerg : 최상위. 매우 위험한 상황.
  info : 단순한 프로그램에 대한 정보 메시지
  notice : 에러가 아닌 알리메 관한 메시지
  warning : 주의를 요하는 메시지
  err : 에러가 발생한 상황
  crit : 급한상황은 아니지만 치명적인 시스템 문제발생 상황
  alert : 최하위, 즉각적인 조치를 ㅜ치해야하는 상황
  none : 어떠한 경우라도 메시지를 저장하지 않는다.

* 로그데몬 컨트롤
# /sbin/syslogd -m 0
# /etc/init.d/syslogd start
# /etc/init.d/syslogd restart
# /etc/init.d/syslogd stop

* 로그구성 및 로그모니터링하기
1. tcpd 로그기록 파일 모니터링
# tail -f /var/log/secure
2. 시스템로그 모니터링
# tail -f /var/log/messages
3. 메일관련 로그 모니터링
# tail -f /var/log/maillog
4. 부팅시의 메시지 확인
# more /var/log/boot.log
# dmesg | more
5. cron 로그 모니터링
# tail -f /var/log/cron

* 마지막 로그인(접속)정보 확인하는 법
# lastlog [-u 계정명] [-t 일자]
lastlog는 /var/log/lastlog파일의 정보에 저장된 정보를 참조하여 결과를 출력한다.
/var/log/lastlog파일에는 각 계정의 최근 접속정보가 기록되는 파일이다.
또한 /usr/include/lastlog.h 파일에 정의된 포맷으로 /var/log/lastlog에 저장이 된다.
즉, /var/log/lastlog파일에 저장되는 포맷은 /usr/include/utmp.h파일에 지정된 포맷을 사용한다는 것을 알 수 있다.
# lastlog -> 시스템 각 계정의 최근 접속정보 확인
사용자명        포트    ~로부터         최근정보
root             tty1                      목  3월 23 16:11:22 +0900 2006
bin                                        **한번도 로긴한적이 없습니다**
daemon                                     **한번도 로긴한적이 없습니다**
adm                                        **한번도 로긴한적이 없습니다**
lp                                         **한번도 로긴한적이 없습니다**
sync                                       **한번도 로긴한적이 없습니다**
shutdown                                   **한번도 로긴한적이 없습니다**
halt                                       **한번도 로긴한적이 없습니다**
mail                                       **한번도 로긴한적이 없습니다**
news                                       **한번도 로긴한적이 없습니다**
uucp                                       **한번도 로긴한적이 없습니다**
operator                                   **한번도 로긴한적이 없습니다**
games                                      **한번도 로긴한적이 없습니다**
gopher                                     **한번도 로긴한적이 없습니다**
ftp                                        **한번도 로긴한적이 없습니다**
nobody                                     **한번도 로긴한적이 없습니다**
rpm                                        **한번도 로긴한적이 없습니다**
vcsa                                       **한번도 로긴한적이 없습니다**
nscd                                       **한번도 로긴한적이 없습니다**
sshd                                       **한번도 로긴한적이 없습니다**
rpc                                        **한번도 로긴한적이 없습니다**
rpcuser                                    **한번도 로긴한적이 없습니다**
nfsnobody                                  **한번도 로긴한적이 없습니다**
mailnull                                   **한번도 로긴한적이 없습니다**
smmsp                                      **한번도 로긴한적이 없습니다**
pcap                                       **한번도 로긴한적이 없습니다**
xfs                                        **한번도 로긴한적이 없습니다**
named                                      **한번도 로긴한적이 없습니다**
widemail         pts/2    58.140.225.64    토  3월 25 23:33:38 +0900 2006
mysql                                      **한번도 로긴한적이 없습니다**
# lastlog -u widemail -> 특정 계정사용자의 시스템 최근 접속정보 확인
사용자명        포트    ~로부터         최근정보
widemail         pts/2    58.140.225.64    토  3월 25 23:33:38 +0900 2006
# # lastlog -t 3 -> 지정한 최근일 까지의 시스템접속정보 확인
사용자명        포트    ~로부터         최근정보
root             tty1                      목  3월 23 16:11:22 +0900 2006
widemail         pts/2    58.140.225.64    토  3월 25 23:33:38 +0900 2006

* 로그파일의 종류
- 콘솔로그(/dev/console)
  kernel에 관련된 내용을 시스템 콘솔에 뿌려주는 로그
- 시스템로그(/var/log/messages)
  이 로그에 기록되는 내용은 주로 접속시의 인증에 관한 것과 메일에 관한 내용, 그리고 시스템에 관한 변경상항 등 시스템에 관한 전반적인 로그를 기록하는 파일이다.
- TCPD로그(/var/log/secure)
  이것은 xinetd에 의한 로그파일이다.
  xinetd의 PID저장 파일은 /var/run/xinetd.pid 이다.
- 메일로그(/var/log/maillog)
  snemail아니 pop등의 실행에 관한 기록이다.
- 크록로그(/var/log/cron)
  시스템의 크론이 작업한 기록을 보관하는 파일이다.
- 부팅로그(/var/log/boot.log)
  시스템의 데몬들이 실행되거나 재시작되었을 때 기록되는 로그파일이다.
- FTP로그(/var/log/xferlog)
  ftp나 ncftp등의 접속이 이루어 졌을 때 이 로그파일에 기록된다.
- 웹로그(/usr/local/apache2/logs/access_log)
  웹서버로 apache 2를 사용하며 apache 2의 설치경로가 /usr/local/apache2 일 때, 웹로그가 기록되는 파일이다.

* syslogd 설치/확인/제거
# syslogd -v -> syslog의 버전을 확인
syslogd 1.4.1
# rpm -qf /etc/syslog.conf -> syslogd의 구체적인 패키지버전을 확인
sysklogd-1.4.1-14.legacy.9
# rpm -ql sysklogd-1.4.1-14.legacy.9 -> 설치되어 있는 파일들의 내용을 확인
/etc/logrotate.d/syslog
/etc/rc.d/init.d/syslog
/etc/sysconfig/syslog
/etc/syslog.conf
/sbin/klogd
/sbin/syslogd
/usr/share/doc/sysklogd-1.4.1
/usr/share/doc/sysklogd-1.4.1/ANNOUNCE
/usr/share/doc/sysklogd-1.4.1/CHANGES
/usr/share/doc/sysklogd-1.4.1/ChangeLog
/usr/share/doc/sysklogd-1.4.1/INSTALL
/usr/share/doc/sysklogd-1.4.1/NEWS
/usr/share/doc/sysklogd-1.4.1/README.1st
/usr/share/doc/sysklogd-1.4.1/README.linux
/usr/share/man/man5/syslog.conf.5.gz
/usr/share/man/man8/klogd.8.gz
/usr/share/man/man8/sysklogd.8.gz
/usr/share/man/man8/syslogd.8.gz
# rpm -Uvh sysklogd-1.4.1-14.legacy.9.rpm -> syslogd 업그레이드
# rpm -e sysklogd-1.4.1-14.legacy.9 -> syslogd 삭제
로그데몬위치 : /sbin/syslogd
로그데몬 설정파일 : /etc/syslog.conf
로그데몬 PID파일 : /var/run/syslog.pid
로그데몬 실행 : /etc/init.d/syslog start
로그데몬 종료 : /etc/init.d/syslog stop
로그데몬 재시작 : /etc/init.d/syslog restart

* 로그데몬의 실행 흐름도
1. init 프로세스에 의한 syslogd 실행
   시스템이 부팅되면서 처음으로 시작죄며, 또는 /etc/rc.dinit.d/syslog start 라는 명령의 수행으로 /sbin/syslogd 의 데몬프로세스가 수행된다.
2. syslog.conf 읽음
   /sbin/syslogd 데몬이 실행이 되면서 /etc/syslog.conf파일을 읽어들이게 된다.
3. syslog.pid 기록
   /sbin/syslogd도 데몬이므로 PID를 /var/run/syslogd.pid에 기록한다.
4. syslog.conf에 기록된 각각의 로그파일들의 로그기로 시작
   /sbin/syslogd의 실행과 함께 syslog.conf파일에 설정되어 있는 각각의 ㄹ그파일들이 기록되기 시작한다.
5. logrotate에 의한 각log파일들 관리

* 로그파일 관리(logrotate)
1. logrotate파일구성
   /usr/sbin/logrotate : 데몬의 위치 및 데몬프로그램
   /etc/logrotate.conf : 설정파일
   /etc/logrotate.d : logrotate에 적용할 각종 로그파일들을 보관하는 디렉토리
   /var/lib/logrotate.status : logrotate한 작업내역을 보관한 파일
   /etc/cron.daily/logrotate : logrotate는 주기적으로 실행이 되어야하므로 cron에 의해 일단윌 실행이 됨
# cat /etc/logrotate.d/syslog
/var/log/messages { -> logrotate에 의해서 작업될 로그파일(messages)을 절대경로로 지정해둔다.
    monthly  -> 로그파일을 순환시킬 주기이며, monthly이므로 한달에 한번씩 순환한다. (가능한 값: daily, weekly, monthly)
    compress  -> 순환된 파일이 gzip에 의해서 압축이 된다.
    rotate 2  -> 순환되는 파일갯수를 지정한다. 0부터 시작하며 monthly로 지정했기 때문에 2달간 ㄹ그파일이 저장된다.
    mail super@superuser.co.kr  -> 순환되어 지정된 갯수를 넘게되는 로그파일은 지정된 메일주소로 메일이 보내지게 된다. 메일을 보내지 않으려면 nomail이라고 한다.
    errors super@superuser.co.kr -> 지정된 log파일의 logrotate작업시에 에러가 발생을 하면 지정된 메일주소로 메일을 발송한다.
    postrotate  -> 지정된 로그파일에 lorotate작업 끝이 난 이후에 실행할 작업을 설정해둔 것이다. logrotate 작업 전에 실행할 작업이 있다면 prerotate/endscript를 사용한다.
        /bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true
    endscript
}
2. logrotage 주요옵션
-f : 강제순환 시킨다. 이 옵션은 새로운 항목을 추가한 후에 로그파일을 순환시키거나 옛날 로그파일이 이미 삭제되어 새로운 로그파일이 생성되어 로그기록이 계속되고 있을 경우에 유용한 옵션이다.
-s, --state <statefile> : 기본 상황파일인 /var/lib/logrotate.status 파일 대신에 지정한 state파일을 사용한다.
--usage : logrotate의 기본 사용법을 간단히 보여준다.
compress : 순한되는 로그파일을 gzip으로 압축한다.
nocompress : 순환되는 로그파일의 압축을 하지 않는다.
create mode owner group : 순환되어 생성되는 로그파일의 파일퍼미션(mode)과 소유자(owner), 그리고 그룹소유자(group)를 지정한 것이다(예, create root 600 wheel)
daily : 로그파일을 이 ㄹ주기로 순환시킨다.
weekly :  로그파일을 주 주기로 순환시킨다.
monthly : 로그파일을 한달 주기로 순환시킨다.
errors addrss : logroate작업시에 에러가 발생한다면 지정된 메일주소로 메일을 보낸다.
extension et : logroate 실행 후에 순횐되어 생성되는 파일의 이름 뒤에 확장자로 붙일 확장자명을 지정한다.
ifempty : 로그파일이 비어있는 경우에도 rotate(순환)을 한다. 기본값
notifempty : ifempty와는 반대로 로그파일이 비어있을 경우에는 순환을 하지 않는다.
mail address : logrotate 작업 후에 이전 로그파일을 지정된 메일주소로 메일을 보낸다.
postrotate/endscript : logrotate 작업 이후에 지정된 작업을 실행한다.
prerotate/endscript : logrotate 작업 이전에 지정된 작업을 실행한다.
rotate count : logroate의 실행결과 순환되는 파일들의 총 갯수이다. 0이면 이전파일은 순환과 함께 삭제된다.
size size : logroate의 결과 순환된 결과 파일사이즈가 지정환 크기를 넘지 않도록 한다.

* Webalizer를 이용한 멀티웹로그 자동 분석법
-- 설치
1. http://www.webalizer.com/ 에서 다운로드한다.
2. Webalizer는 gd라이브러리가 필요하다. 설치되어 있지 않다면 설치한다.
3. tar xfz webalier-2.01-10-src.tgz
4. cd webalier-2.01-10
5. ./configure --with-language-korean
6. make
7. make install
-- 환경구성
1. 실행설정파일 만들기
# cd /usr/local/webalier
# mkdir conf
# cd conf
# cp /etc/webalizer.conf.sample home.org.conf
# cp /etc/webalizer.conf.sample home.net.conf
# home.org.conf 에서 다음의 항목을 수정한다.
  LogFile : 웹로그파일 위치 (예: /home/home.org/www_log/access_log)
  OutputDir : 부석결과 저장파일 (예: /home/home.org/www/weblog)
  HistoryNam : 사이트명 (예: dhan.org)
  ReportTitle : 분석결과 페이지 이름 (예: DHAN ORG WEBSITE)
  Hostname : 호스트명 FQDN 형식(예: http://www.dhan.org/)
2. Webalizer 실행파일 생성
# cat /usr/local/webalizer/bin/webalizer.sh
/usr/local/bin/webalizer -c /usr/local/webalizer/conf/dhan.org.conf
/usr/local/bin/webalizer -c /usr/local/webalizer/conf/dhan.net.conf
3. 자동분석 설정을 위한 cron 설정
# crontab -l
00 03 * * * su - root -c '/usr/local/webalizer/bin/webalier.sh'
3. 웹로그 분석결과 확인하기
http://도메인/weblog 에서 확인한다. (예: http://www.dhan.org/weblog)

* 로그서버 구축 및 운영
-- 대상서버들(로그 클라이언트) 내부 작업
로그클라이언트는 내부에서 다음의 작업을 한다.
1. /etc/hosts에 로그서버의 IP와 호스트이름을 지정한다.
# cat /etc/hosts
127.0.0.1 localhost.localdomain localhost
192.168.5.12 loghost
2. /etc/syslog.conf 파일 수정
# cat /etc/syslog.conf | grep loghost
*.info;mail.none;authpriv.none  @loghost
authpriv.*    @loghost
*.*     @loghost -> 모든 로그를 loghost에 보내는 경우에만 설정
3. 로그데몬 재시작
# /etc/init.d/syslog restart
-- 로그서버내부 작업
1. /etc/host 파일에 로그대상서버들 설정
# cat /etc/hosts
192.168.0.1 web1 web1.dhan.org
192.168.0.2 web2 web2.dhan.org
192.168.0.3 db db.dhan.org
2. 로그서버의 syslogd 데몬을 재시작한다.
   리눅스 서버의 syslogd 데몬은 기본이 원격지의 로그를 받아들이지 않는다.
   따라서 원격지의 로그를 받아들여서 로그서버의 로그파일에 기록을 허용하도록 -r 옵션과 함께 syslogd 를 수작업으로 실행시켜야 한다.
   다른 방법은 /etc/init.d/syslog 를 수정하는 것이다.
# /sbin/syslogd -r -m 0
# grep SYSLOGD /etc/sysconfig/syslog
SYSLOGD_OPTIONS="-m 0" -> SYSLOGD_OPTIONS="-r -m 0"

***********************************************************
* 인터넷 수퍼데몬 xinetd
***********************************************************

* chkconfig
# chkconfig 서비스명 on  -> 실행레벨 2, 3, 4, 5 에서 해당 서비스를 각각 활성화한다.
# chkconfig 서비스명 off -> 실행레벨 2, 3, 4, 5 에서 해당 서비스를 각각 비활성화한다.
# chkconfig 서비스명 --add -> chkconfig 관리대상에 해당 서비서를 등록한다.
# chkconfig 서비스명 --del -> chkconfig 관리대상에서 해당 서비스를 제거한다.
# chkconfig --list  -> 모든 서비스들의 각 실행레벨에 따른 on/off 상황을 전부 보여준다.
-- example
# chkconfig telnet on  -> telnet 서비스를 실행레벨 2, 3, 4, 5번에서 각각 on한다.
# chkconfig telnet off  -> telnet 서비서를 실행레벨 2, 3, 4, 5번에서 각각 off한다.
# chkconfig telnet --list -> telnet 서비스에 대한여 on/off 상황을 보연준다.
# chkconfig --list telnet
# chkconfig --level 3 5  -> 실행레벨 3과 5에 대하여 각각 on/off된 서비스 내역을 보여준다.

* xinetd의 특징
- xinetd는 inetd 상위버전으로 레드햇 7.0이후 버전부터 사용되었다.
- 각각의 서비스별로 별도의 파일에 설정이 가능하다. (/etc/xinetd.d/ 디렉토리에 있는 파일들)
- xinetd에서 가지고 있던 접근제어기능을 가지고 있다.
- tcp_wrapper를 내장하고 있기 때문에 접근제어를 할 수 있다.
- timeout 설정으로 서비스 접근제어를 할 수 있다.
- 접속시도 횟수로 접근제어를 할 수 있으므로 무차별 서비스거부공격(DoS)을 방지할 수 있다.
- 동일한 IP를 가진 호스트에서 동시 접속수를 제어하여 즙근제어를 할 수 있다.
- 로그파일의 크기를 재한할 수 있다.
- xinetd에서 제어되는 각 서비스들에 대한 syslog 로깅 레벨 설정이 가능하다.
- 접속하는 클라이언트의 서비스 이용시간을 기록할 수 있다.
- 서비스를 거부하거나 서비스 접근제어가 되었을 경웨 상세로그를 기록한다.

* 리눅스 서버의 서비스 관리방식들
- init프로세스에 의한 관리
  init 프로세스 설정파일 : /etc/inittab
- SYSTEM V 스크립트방식
  /etc/rc.d/init.d/ 디렉ㅌ리에 존재하는 각 서비스 제어파일에 의한 관리방식
- 리눅스 명령어(command)에 의한 관리방식
  가장 직접적인 방식
- xinetd에 의한 관리방식
  /etc/xinetd.d/ 디렉토리내의 각 서비스파일들에 의한 관리방식
- 서비스 제어 흐름도
  CSU <-> ROUTER <-> 방화벽 <-> 스위치 <-> 리눅스 서버내부(iptables <-> xinetd한경서비슽 <-> /etc/xinetd.c/설정파일에 의한 서비스 연결(pop3, telnet)

* xinetd 서비스 흐름도(telnet의 경우)
1. 외부에서 telnet 서비스의 요청이 들어온다.
2. xinetd데몬은 외부에서 요청된 서비스를 tcpd에 넘겨준다.
3. tcp는 /etc/hosts.allow와 /etc/hosts.deny파이을 검사하여 허가된 접속인지를 검사한다.
4. 3번의 검사에 의해 허가된 요청일 경우에는 /etc/syslog.conf에 설정되어 있는 /var/log/secure파일에 접속요청에 대한 정보를 기록하고 /etc/xinetd.d/telnet파일을 불러들여서 서비스를 연결한다.
   이 때, /etc/xinetd.d/telnet파일내의 server 지시장행에 설정되어 있는 telnet 데몬의 위치를 찾아서 요청을 처리한다.
5. 3번의 검사에 의해 허가되지 않은 요청일 경우에는 /etc/syslog.conf파일에 설정되어 있는 /var/log/secure 파일에 허가되지안흥ㄴ 접속이 있었다는 로그를 남기고 접속요청을 거부한다.

* xinetd에 관련된 파일들
/etc/xinetd.conf : xinetd 서비스에 공통적으로 적용되는 주된 설정파일
/etc/xinetd.d/서비스파일들 :xinetd로 서비스될 파일들이 존재하는 디렉토리
/usr/sbin/xinetd : xinetd데몬
/etc/services  : 서비스포트 설정파일
/etc/protocols  : 프로토콜 설정파일
/etc/syslog.conf : 시스템로그 설정파일
/usr/sin/tcpd  : tcp데몬
/etc/hosts.allow : 시비스별 허용목록파일(tcpd)
/etc/hosts.deny  : 서비스별 거부목록 파일(tcpd)
/var/log/secure  : tcpd 로그파일(접속기록 파일)
/etc/init.d/xinetd : xinetd 시작/종료 스크립트파일

* xinetd에서 사용하는 서비스이름
1. chkconfig 명령어에서 사용하는 서비스명
2. "service 서비스데몬명 start"에서 사용하는 서비스데몬명
3. /etc/hosts.allow파일과 /etc/hosts.deny파일 내에서 사용하는 서비스명
4. /etc/xinetd.d/디렉토리내에서 사용하는 서비스파일명
5. /etc/xinetd.d/디렉토리내에 존재하는 각 파일들의 내부에 설정된 "service 서비스명"에서 설정하는 서비스명
6. /etc/services파일내에서 사용하는 서비스명
규칙1: 위의 1번부터 6번까지의 각 서비스명(이름)들은 동일하게 사용하지 않아도 된다.
규칙2: /etc/hosts.allow파일과 /etc/hosts.den파일내에서 사용하는 서비스이름은 실제 데몬파일명을 지정해야 한다.
       예를 들어 "sshd : ALL"에서 sshed라는 이름은 /usr/sbin/sshd라는 파일명에서 가져온 것이다.

* /etc/xinetd.d/ 디렉토리내의 각 설정파일내에 설정가능한 지시자들
# cat /etc/xinetd.d/telnet
# default: on
# description: The telnet server serves telnet sessions; it uses \
#       unencrypted username/password pairs for authentication.
service telnet
{
 disable  = no
 flags  = REUSE
 socket_type = stream
 wait  = no
 user  = root
 server  = /usr/sbin/in.telnetd
 log_on_failure += USERID
}
service : 서비스 이름입니다. 가능한 파일이름과 /etc/services파일에 등록딘 서비스이름과 동일해야 한다.
disable : 해당 서비스를 서비스할 것인가(no) 아닌가(yes)를 결정한다.
          즉, 서비스를 하도록 설정하려면 no로 설정하고 서비스를 하지 않으려면 yes로 설정한다.
socket_type : tcp일 경우에는 strea이며, udp일 경우에는 dgram이라고 명시한다. (가능한 값은 raw, rdm, seqpacket 등이 있다)
wait : xinetd가 서비스 요청을 받은 경우, 이후에 즉시 또 다른 요청을 처리할 것인지(no) 아닌지(yes)의 여부를 결정하는 지시자이다.
       stream일 경우에는 반드시 no이어야 한다.
       no 는 현재 요청외에는 다른 접속요청을 새로운 것으로 시작하여 처리하게 된다.
user : 이 서비스를 어떤 사용자 권한으로 서비스할 것인가를 결정한다.
server : 이 서비스를 실행할 때에 어느 위치에 있는 프로그램(데몬)을 불러와 연결할 것인가를 명시한 것이다. 반드시 절대경ㄹ로 지정해야 한다.
log_on_failuer : 서버접속에 성ㄱㅇ하지 못하였을 때 ㄹ그파일에 기록하는 내용들을 설정할 수 있다.
                 여기에는 HOST, USERID 그리고 ATTEPMT, RECORD 등이 추가로 설정될 수 있다.
   HOST란 접속을 시도한 클라이언트의 IP주소를 의미한다.
   USERID란 접속한 사용자의 ID를 의미한다.
   +=는 /etc/xinetd.conf파일의 기본설정항목에 추가할 항목을 지정할 때 사용한다.
   -=는 /etc/xinetd.conf파일의 기본설정항목에서 뺄 항목을 지정할 때 사용한다.
log_on_success : 서버 접속에 성공하였을 경웨 기록할 내용을 설정할 수 있다.
                 이 설정에는 PID, HOST, USERID, EXIT, DURATION 등을 기록할 수 있다.
   여기서 PID란 프로세스의 ID를 의미한다.
   HOSTID란 클라이언트의 IP주소를 의미한다.
   USERID란 접속한 사용자의 ID를 의미한다.
   EXIT란 프로세스의 종료상태를 의미한다.
   DURATION이란 연결지속시간을 의미한다.
only_from : "only_from = 192.168.1.0/24"아 같은 설정이 가능하다. 즉, 현재 이 서버로 해당 서비스(telnet) 접속이 가능한 곳은 192.168.1.0 네트워크내의 호스트들로 제한된다.
no_access : "no_access = 192.168.1.100"와 같은 설정이 가능하다. 즉, 접속 가능한 호스트 중에서 192.68.1.100번 IP주소를 가진 호스트는 제외하여 접속하지 못하게 한다.
instances : "instances = 60"와 같은 설정이 가능하다.즉, 해당 서비스(telnet)로 접속이 가능한 총 접속자수는 60명까지로 제한한다.
access_time : "access_time = 08:00-18:00"와 같은 설정이 가능하다. 즉, 해당 서비스의 이용가능 시간을 오전 8시부터 오후 6시까지로 제한한다.
per_source : "per_source = 5"와 같은 설정이 가능하다. 즉, 동일한 호스트에서 해당 서비스로의 접속시에 동시에 5번 이상 접속할 수 없다. 이 서비스는 서비스거부공격(DoS)을 차단하기 위해 사용할 수 있다.

* xinetd 시작/종료
# /etc/init.d/xinetd start -> xinetd 시작
# /etc/init.d/xinetd stop -> xinetd 종료
# /etc/init.d/xinetd restart -> xinetd 재시작
# /etc/init.d/xinetd status -> xinetd 상황점검

* xinetd에 의한 접속제어(tcpd)
tcpd는 특정 IP나 도메인으로부터 서버로의 telnet, ftp, pop 등의 접속을 차단할 있으며, 접속기록이나 접속시도기록을 특정한 파일에 로그로 기록한다.
xinetd는 tcp-wrapper를 내장하고 있기 때문에 xinetd모드에서 실행되는 서비스들(예: ftp, telnet, ssh)은 거의 대부분 tcpd라는 tcp_wrapper의 데몬에 의해 접속제어를 받게 된다.
쉽게 말해서 개별 서비스들(예: ftp, telnet 등)의 접근허용을 설정하는 파일은 /etc/hosts.allow이며 접근허용이 되지 않도록 설정하는 파일은 /etc/hosts.deny 파일이다.
--
접근제어 대상서비스 : xinetd기반에서 제어되는 모든 서비스명들로서 /etc/services내의 각 서비스이름과 /etc/xinetd.d/디렉토리에 존재하는 파일명과 동일한 이름을 사용한다. (예: telnet, sshd, proftpd)
접근제어 대상 : 특정 도메인명, 특정 호스트명, 특정 IP주소, 특정 네트워크 (예: superuser.co.kr, host1, 192.168.0.100, 192.168.1.0/24, 192.168.2.0/255.255.255.0)
실제예)
ALL : ALL
proftpd : 192.168.10.0/255.255.255.0 EXCEPT 19.168.10.100
proftpd : 192.168.0.100 : twist /bin/echo "Access Denied!"
sshd : 192.168.0.100
sshd ; 192.168.1.0/24
in.telnetd : 210.101.112.240 210.101.112.241 211.123.12.12 168.126.3.
in.ftpd :    210.101.112.240 210.101.112.241 211.123.12.12 168.126.3.
ipop3d : ALL
--
- 접근제어 대상서비스와 접근제어 대상은 콜론(:)으로 구분한다. 또한 세번째 항목을 계속 설정할 때에도 콜론으로 구분하여 설정한다.
- /etc/hosts.allow내의 설정과 /etc/hosts.deny내의 설정이 중복되었을 경우에 /etc/hosts.allwo파일의 내용이 적용된다.
- 각 대상리스트들의 나열은 스페이스(space)나 콤마(,)로 구분한다.

* /etc/services 파일
- 형식
서비스이름 포트/사용프로토콜유형 별칭
서비스이름 : 설정되는 포트에 대한 서비스의 이름이다. (예: telnet, ftp 등)
포트/프로토콜 : 지정된 서비스에서 사용될 포트번호와 사용할 프로토콜유형이다. 프로토콜유형은 일반적으로 tcp와 udp등이 사용된다.
별칭 : 지정된 "서비스이름"외에 다른 이름을 사용할 수 있도록 한다.

* /etc/protocols 파일
/etc/services 파일에서 사용하는 프로토콜의 정의가 있는 파일이다.
# cat /etc/protocols
..
tcp     6       TCP             # transmission control protocol
..
udp     17      UDP             # user datagram protocol
..

***********************************************************
* RPM 패키지 관리
***********************************************************

* rpm 명령어의 실행 종류
패키지 설치 : rpm -i [옵션] 패키지명
패키지 업그레이드 : rpm -U [옵션] 패키지명
패키지 제거(삭제) : rpm -e [옵션] 패키지명
패키지 질의 : rpm -q [옵션] 패키지명
패키지 검증 : rpm -V [옵션] 패키지명
              rpm -y [옵션] 패키지명
패키지 제작 : rpm -b [옵션] 패키지명세파일
              rpmbuild -b [옵션] 패키지명세파일

* rpm 패키지 명명방법
tcp_wrapper-7.6-34.i386.rpm
패키지이름 : tcp_wrapper
    패키지이름은 제작자에 의해 붙여진 이름으로 대부분은 특정 서비스를 나타내는 이름이거나 특정 라이브러리를 의미하는 이름을 붙이는 것이 관례이다.
패키지버전 : 7.6
    패키지의 버전을 의한다.
릴리즈버전 : 34
    동일한 패키지이름의 동일한 패키지버전을 업데이트하여 배포할 때마다 새롭게 붙이는 버전이다.
    릴리즈버전을 붙이는 이유는 주로 동일한 패키지에서 버그를 수정하였거나 간단한 업그레이드를 하였을 경우에 패키지버너을 높이지않고 릴리즈버전만 높여서 패보하기 때문이다.
아키텍처번 : i386
    이 패키지의 설치가 가능한 컴퓨터 아키텍쳐를 의미한다.

* rpm 패키지 설처
# rpm -i [옵션] 패키지명
# rpm -ivh [옵션] 패키지명
  -v : verbose옵션으로 자세한 설치과정을 보여준다.
  -h : 설치과정을 "#"표시를 하면서 그 진행과정을 표시한다.
# rpm -ivh --replacepkgs 패키지명
  --replacepkgs 옵션은 기존의 설치되어 있던 패키지를 무시하고 다시 설치하게 한다.
  이 옵션은 기존의 설정과 파일들을 모두 삭제한다.
  이미 설치된 패키지 무시하고 패키지 설치하기
# rpm -iv --replacefiles 패키지명
  --replacefiles 는 패ㅣ지의 일부 파일과 이미 설치되어 있는 다른 패키지의 일부 파일과 중복되어 설치가 되지않을 경우 사용하며, 기존의 파일을 무시하고 새 파일로 설치한다.
  다른 패키지의 파일과 중복될 경우에 무시하고 설치하기
# rpm -ivh --force 패키지명
  --force 옵션은 --replacepkgs 옵션과 --replacefiles 옵션, 그리고 --oldpackage라는 옵션을 모두 사용하는 것돠 동일한 효과를 가진다.
  --oldpackage라는 옵션은 현재 설치하려고 하는 패키지의 번전이 이미 설치디어 있는 패키지의 버전보다 낮은 버전일 경우에 에러가 발생한다.
  패키지중복 또는 다른 패키지의 파일이 충돌발생시 무시하고 설치하기
# rpm -ivh --nodeps 패키지명
  --nodeps는 의존성문제를 무시하고 설치하기 위한 옵션이다.
  패키지의 의존성문제를 무시하고 패키지 설치하기

* rpm 패키지 업그레이드
# rpm -U [옵션] 패키지명
# rpm -Uvh [옵션] 패키지명
  -U 옵션을 사용하여 설치하면 기존에 설치되어 있던 RPM 패키지의 파일들을 삭제하는 것이 아니라 "기존파일명.rpmsave"라는 파일명으로 보관한 후에 새롭게 설치한다.
# rpm -Uvh --replacepkgs 패키지명
  이미 설치된 중복패키지를 무시하고 업그레이드하기
# rpm -Uvh --replacefiles 패키지명
  다른 패키지의 파일가  중복될 경우에 무시하고 업그레이드하기
# rpm -Uvh --force 패키지명
  패키지중복 또는 패키지의 파일이 충돌발생시 무시하고 업그레이드하기
# rpm -Uvh --nodeps 패키지명
  패키지의 의존성문제를 무시하고 패키지 업그레이드하기

* rpm 패키지 삭제
# rpm -e 패키지명
# rpm -e --nodeps 패키지명
  의존성을 무시하고 삭제하기

* rpm 패키지 설치정보 확인
# rpm -q [옵션] 패키지명
# rpm -qa
  현재 시스템에 설치디어 있는 모든 패키지를 출력
# rpm -qf 파일명
  특정 파일명이 어떤 패키지에 속해있는지 확인한다.
# rpm -ql 패키지명
  특정 패키지에 의해 설치된 파일들을 확인한다.
# rpm -qi 패키지명
  특정 패키지의 상세한 정보 확인하기.
# rpm -qs 패키지명
  특정 패키지에 의해 설치된 파일들의 정상여부 점검하기.
# rpm -qc 패키지명
  특정 패키지의 설정파일들 확인하기.
# rpm -qd 패키지명
  설치된 패키지의 문서파일위치 확인하기
# rpm -qRp rpm패키지파일
  패키지 의존성 관계 확인하기

* 설치딘 rpm 패키지 변경가능성 여부 검증하기
설치한 패키지의 무결성을 확인하는 방법
# rpm -V [옵션] 패키지명

***********************************************************
* FTP 서버 (vsftpd)
***********************************************************

* vsftpd 관련 파일들
/etc/xinetd.d/vsftpd  xinetd 환경에서 서비스하기 위한 vsftpd의 xinetd 설정파일
/etc/vsftpd/vsftpd.conf  vsftpd의 주된 설정파일.
/usr/sbin/vsftpd  vsftpd의 데몬파일.
/etc/initd./vsftpd  vsftpd 시작/종료/재시작 스크립트.
/etc/pam.d/vsftpd  vsftpd의 PAM설정
/var/log/vsftpd.log  vsftpd의 로그파일
/etc/vsftpd/user_list  ftp접속을 제한할 사용자 리스트
/etc/vsftpd/ftpusers  PAM에서 사용하는 ftp접속제한자 리트스파일. 이 파일에 등록된 사용자들은 ftp접속을 할 수 없다.

* 계정별 vsftpd 접속제한 파일
1. /etc/vsftpd/user_list
   vsftpd에서 기본으로 사용하는 계정별 접속제어 파일은 /etc/vsftpd/user_list(or /etc/vsftpd.user_list)파일이다.
   이 파일에 등록된 사용자들은 ftp접속을 거부하게 된다.
   이 파일에 등록할 때에는 다으 3가지 룰을 지켜주면 된다.
   - 가능한 시스템 계정들은 모두 등록한다.
   - 한 행에 한 계정씩만 등록한다.
   - /etc/vfstpd/vsftpd.conf파일(or /etc/vsftpd.conf)에서
     "userlist_deny=NO"으로 설정되었을 경우에 여기에 등록한 사용자들은 접속을 허용하는 사용자들이며
     "userlist_deny=YES"로 설정하였을 경우에는 접속을 거부하는 사용자리스트가 된다.
     YES가 기본이므로 기본설정을 변경하지 않는다면 이 파일은 접속거부자리스트파일이 된다.
2. /etc/vsftpd/ftpusers
   다음은 PAM의 VSFTPD설정파일로서 /etc/vsftpd/ftpusers파일에 등록된 사용자들은 접속을 거부하겠다는 의미의 설정파일이다.
   즉, /etc/vsftpd/ftpusers파일은 PAM에서 설정하여 사용하고 있는 ftp접속거부자 이스트파일이다.
#cat /etc/pam.d/vsftpd
#%PAM-1.0
auth       required     pam_listfile.so item=user sense=deny file=/etc/vsftpd/ftpusers onerr=succeed
auth       required     pam_stack.so service=system-auth
auth       required     pam_shells.so
account    required     pam_stack.so service=system-auth
session    required     pam_stack.so service=system-auth

* /etc/vsftpd/vsftpd.conf 설정옵션
- anonymous_enable=YES (default YES)
  익명(anonymous)접속을 허용할 것인(YES) 허용하지 않을 것인가(NO)를 결정한다.
- anon_root=/var/ftp
  익명(anonymous)접속시 루트디렉토리를 지정한다.
- local_enable=YES (default YES)
  로컬 계정 사용자들의 접속을 허용할 것인가의 여부를 결정한다.
  YES로 설정하면 로컬계정사용자의 접속을 허용하는 것이며 NO로 설정하면 허용하지 않는 것이다.
- write_enable=YES (default YES)
  이 설정은 ftp전용명령어 중에 write명령어를 허용할 것인가를 결정하는 것이다.
  허용하려면 YES, 허용하지 않으려면 NO를 설정한다.
- local_umas=022 (default 022)
  로컬계정 사용자들의 umask값을 설정한다.
- anon_upload_enable=YES (default NO)
  익명(anonymous)계정 사용자에게 파일 어로드를 허용할 것인가(YES) 허용하지 않을 것인가(NO)의 여부를 설정한다.
  가능한 익명계정으로 접속한 사용자에게는 업로드 권한을 허용하지 않는 것이 보안에 좋다.
- anon_mkdir_write_enable=YES (default NO)
  익명(anonymous)계정 사용자에게 디렉토리 생성권한을 허용할 것인가(YES) 허용하지 않을 것인가(NO)의 여부를 설정하는 지시자이다.
  가능한 익명계정으로 접속한 사용자에게는 디렉토리 생성권한을 허용하지 않는 것이 보안에 좋다.
- ftpd_banner=Welcome to blah FTP service.
  ftp서버로 접속할 때에 안내메시지등을 출력하려면 여기서 설정하면 된다.
- dirmessage_enable=YES
  ftp접속한 사용자가 특정 디렉토리로 이동하였을 때 개별 디렉토리의 메시지를 보여주도록 허용할 것인가(YES) 허용하지 안을 것인가(NO)를 설정하다.
- message_file=.message
  ftp접속후에 특정 디렉토리로 이동할 때에 디렉토리 안내메시지 파일로 사용할 파일명을 지정한다.
- xferlog_enable=YES
  ftp접속후에 파일 업로드와 다운로드에 대한 로그를 남길것인가(YES) 남기지 않을 것인가(NO)를 설정한다.
- xferlog_file=/var/log/vsftpd.log
  ftp로그파일의 위치를 결정한다.
- xferlog_std_format=YES
  로그파일에 남길 로그파일의 포맷을 기본포맷으로 남길 것인가(YES) 아닌가(NO)를 설정한다.
- connect_from_port_20=YES
  ftp서비스는 깁ㄴ적으로 21번 포트와 20번 포트를 사용한다.
  ftp 접속돠 명령어에 사용되는 포트는 21ㅓㄴ이며 실제 데이터전송에 사용되는 기본포트는 20번이다.
  이 때 20번 포트의 데이터전송 연결을 허용할 것인가(YES) 허용하지 않을 것인가(NO)를 설정한다.
- session_support=YES
  이 설정이 YES로 설정되어 유효하게 되었을 때에는 바이너리파일인 wtmp에 ftp접속관련기록을 남기게 된다.
  last라는 명령어는 각 사용자들의 접속기록을 wtmp파일에서 가져와 확인하는 명령어이므로 이 설정이 적용되면 last명령어로 ftp접속기록을 확인할 수 있다.
- idle_session_timeout=600
  ftp연결에서 idle타임에 대한 타임아웃값을 설정한다.
- data_connection_timeout=120
  데이터 전송시 적용되는 타임아웃값을 설정한다.
- anon_max_rate=0
  local_max_rate=0
  trans_chunk_size=0
  ftp서비스의 전송속도를 제한하는 설정이다.
  초당 byte수를 지정할 수 있으며 제한없이 허용하려면 0으로 설정한다.
  이 설정은 vsftpd가 독립뎀ㄴ(standalone)모드로 서비스될 때에만 적용되는 것이다.
- max_clients=30
  max_per_ip=3
  이 설정은 동시 ftp접속자수를 제한하는 설정이다.
  max_clients는 ftp접속을 최대 30명까지만 허용하는 설정이다.
  max_per_ip는 한 IP(호스트)에서 동시에 3번까지만 접속이 가능하다는 설정이다.
- ascii_upload_enable=YES
  ascii_download_enable=YES
  기본적으로 ASCII모드로 업로드/다운로드하는 것은 제한되어 있다.
  이 설정으로 ASCII모드로의 업로드/다운로드를 허용하도록 설정할 수 있다.
- deny_email_enable=YES
  banned_email_file=/etc/vsftpd/banned_emails
  익명접속시에 기본적으로 사용되는 계정명을 anonymous이며 패스워드는 email형식으로 입력하면 된다.
  이 때 패스워드로 인정하지 않을 즉, 패스워드로 사용하지 못하도록 할 email 주소를 사용하도록 하는 지시자이다.
  즉, "deny_email_enable=YES"로 설정하시고 "banned_email_file=/etc/vsftpd/banned_emails"로 설정되어 있다면
  패스워드로 허용하지 않을 email 주소를 /etc/vsftpd/banned_emails 파일에 넣어두면 된다.
  그러면 이 파일에 등록된 email주소는 패스워드로 인정하지 않는다.
- chroot_list_enable=YES
  chroot_list_file=/etc/vsftpd/chroot_list
  전체 사용자가 아닌 특정 사용자들에 대하여 자신의 홈디렉토리를 루트디렉토리로 인식하도록 하는 기능으로서 이 기능은 사용자의 홈디렉토리의 상위디렉토리로 벗어나지 못하도록 하는 설정이다.
  먼저 "chroot_list_enable=YES"로 설정하고 /etc/vsftpd/chroot_list 파일에는 이 기능을 적용할 사용자계정명을 등록하면 된다.
  즉, /etc/vsftpd/chroot_list 파일에 등록된 사용자들에 한하여 chroot기능이 적용되어 자기 자신의 홈디렉토리 상위 디렉토리의 이동이 제한된다.
- chroot_local_user=YES
  특정 사용자가 아닌 전체 사용자를 대상으로 chroot()기능을 적용하여 자기 자신의 홈디렉토리 상위 디렉토리로 이동하지 못하도록 하려면 이 설정을 YES로 설정한다.
  만약 "chroot_list_enable=YES" 이고 "chroot_local_user=YES"이면 /etc/vsftpd/chroot_list파일에 등록된 사용자들만 chroot()적용을 받지 않게 된다.
  즉, 이 두 설정이 모두 YES로 되어있다면 /etc/vsftpd/chroot_list에 등록된 사용자들을 제외한 나머지 사용자들만 chroot()가 적용된다.
- ls_recurse_enable=YES
  ftp접속에서는 ls 사용시 -R옵션을 허용하지 않는 것이 기본 설정이다.
  -R옵션이란 서브디렉토리내의 파일들의 리스팅(목록)까지 모두 확인할 수 있도록 하는 것이다.
  이 지시자의 값이 YES로 되어 있다면 ftp접속후에 디렉토리 목록 확인시에 서브디렉토리들의 목록들까지 하넌에 볼 수 있는 -R옵션을 허용하게 된다.
- pam_service_name=vsftpd
  vsftp에서 PAM설정파일명으로 사용할 파일명을 지정한다.
- listen=YES
  listen_port=21
  만약 vsftpd를 xinetd모드가 아닌 독립데몬(standalone)으로 서비스하려면 위의 listen지시자를 YES로 설정하고 listen_port에 서비스할 포트번호(기본 21번)를 지정하면 된다.

* ftp 전용명령어 요약
ascii : 전송모드를 ASCII모드로 설정한다.
binary : 전송모드를 BINARY로 설정한다.
bell : 명령어 완료시에 벨소리를 나게한다.
bye : ftp접속을 종료하고 빠져나간다.
cd : remote시스템의 디렉토리를 변경한다.
cdup : remote시스템에서 하단계 사우이디렉토리로 이동한다.
chmod : remote시스테의 파일퍼미션을 변경한다.
close : ftp저속을 종료한다.
delete : remote시스템의 파일을 삭제한다.
dir : remote시스템의 디렉토리 내용을 출력한다.
disconnect : ftp접속을 종료한다.
exit : ftp접속을 종료하고 빠져나간다.
get : 지정된 파일 하나늘 가져온다.
hash : 파일전송 도중에 "#"표시를 하여 전송중임을 나타낸다.
help : ftp명령어 도움알을 볼 수 있다.
lcd : local시셈의 디렉토리를 변경한다.
ls : remote시스템의 디렉토리 내용을 출력한다.
mdelete : 여러개의 파일을 한꺼번에 지울 때 사용한다.
mget : 여러개으 파일을 한꺼번에 가져오려할 때 사용한다.
mput : 한꺼번에 여러개의 파일을 remote시스템에 올린다.
open : ftp접속을 시도한다.
prompt : 파일전송시에 확인과정을 거친다. on/off 토글
put : 하나의 파일을 remote시스템에 올린다.
pwd : remote시스템의 현재 작업디렉토리를 표시한다.
quit : ftp접속을 종료하고 빠져나간다.
rstatus : remote시스템의 상황(version, 어디서, 접속ID등)을 표시한다.
rename : remote시스템의 파일명을 바꾼다.
rmdir : remote시스템의 디렉토리를 삭제한다.
size : remote시스템에 있는 파일의 크기를 byte단위로 표시한다.
status : 현재 연결된 ftp세션 모드에 대한 설정을 보여준다.
type : 전송모드를 설정한다.

* ncftp
- ncftp를 이용한 원격서버로의 익명(anonymous) ftp접속하기
# ncftp
NcFTP 3.1.9 (Mar 24, 2005) by Mike Gleason (http://www.NcFTP.com/contact/).
ncftp> open ftp://ftp.bora.net/
Connecting to ftp://ftp.bora.net/...                                                                                               
ProFTPD 1.2.7 Server (ftp://ftp.bora.net/) [ftp5]
Logging in...                                                                                                               
Anonymous access granted, restrictions apply.
Logged in to ftp://ftp.bora.net/.
ncftp / >
# ncftp ftp://ftp.bora.net/
NcFTP 3.1.9 (Mar 24, 2005) by Mike Gleason (http://www.NcFTP.com/contact/).
Connecting to ftp://ftp.bora.net/...                                                                                               
ProFTPD 1.2.7 Server (ftp://ftp.bora.net/) [ftp5]
Logging in...                                                                                                               
Anonymous access granted, restrictions apply.
Logged in to ftp://ftp.bora.net/.                                                                                                  
ncftp / >
- 원격서버의 계정사용자를 지정하여 저속하기
# ncftp -u dhan ftp://ftp.bora.net/
- 원격서버의 계정사용자 패스워드 지정하여 자동접속하기
# ncftp -u dhan -p dhan ftp://ftp.bora.net/
- 특정포트를 지정하여 원격서버 접속하기
# ncftp -u dhan -P 2121 ftp://ftp.bora.net/
- 원격서버의 특정파일을 로컬서버로 가져오기
ncftp /home/widemail/a > get spas.sql
- 원격서버의 여러개의 파일을 한번에 로컬서버로 가져오기
ncftp /home/widemail/a > mget *.html
- 원격서버의 특정 디렉토리내의 모든 파일을 한번에 로컬서버로 가져오기
ncftp /home/widemail/a > mget *
- 원격서버의 특정 디렉토리내의 모든 파일과 서브디렉토리를 한번에 가져오기
ncftp /home/widemail/a > mget -R www
- 원격서버의 현재 디렉토리내의 모든 파일과 서브디렉토리들을 한번에 가져오기
ncftp /home/widemail/a > mget -R *
- 원격서버내의 특정 파일 내용 확인하기
ncftp /home/widemail/a > cat README
- 원격서버내의 특정 파일 내용을 한 페이지씩 확인하기
ncftp /home/widemail/a > page README
- 로컬서버내의 특정 파일 내용을 한 페이지씩 확인하기
ncftp /home/widemail/a > lpage INSTALL
- 로컬서버의 현재 작업디렉토리 확인하기
ncftp /home/widemail/a > lpwd
- 로컬서버의 현재 작업디렉토리 내에 파일리스트 확인하기
ncftp /home/widemail/a > lls
ncftp /home/widemail/a > lls -l
ncftp /home/widemail/a > lls -al
ncftp /home/widemail/a > lls -alR
- 로컬서버 내에 존재하는 특정파일명 변경하기
ncftp /home/widemail/a > lreanem file1 newfile
- 원격서버내의 특정 파일 삭제하기
ncftp /home/widemail/a > rm list
- 로컬서버내의 특정 파일 삭제하기
ncftp /home/widemail/a > lrm local_list
- 로컬서버내의 특정 파일 퍼미션 변경하기
ncftp /home/widemail/a > lcmod 777 local_list
- ㄹ컬서버내의 특정 디렉토리 삭제하기
ncftp /home/widemail/a > lrmdir local_dir
- 로컬서버내의 새로운 디렉토리 생성하기
ncftp /home/widemail/a > lmkdir local_new_dir
- ncftp에서 특정 도메인 및 호스트정보 확인하기
ncftp /home/widemail/a > lookup ftp://ftp.bora.net/
ncftp /home/widemail/a > lookup -v ftp://ftp.bora.net/
- ncftp의 북마크 설정과 사용방법
# ncftp -u widemail -p "12345" localhost
NcFTP 3.1.9 (Mar 24, 2005) by Mike Gleason (http://www.NcFTP.com/contact/).
Connecting to localhost...                                                                                                  
(vsFTPd 2.0.3)
Logging in...                                                                                                               
Login successful.
Logged in to localhost.                                                                                                     
ncftp /home/widemail > bookmark
Enter a name for this bookmark, or hit enter for "localhost": spas_dev


You logged into this site using a password.
Would you like to save the password with this bookmark?

Save? [no] yes
Bookmark "spas_dev" saved.
ncftp /home/widemail >

[widemail@dev f]$ ncftp spas_dev
NcFTP 3.1.9 (Mar 24, 2005) by Mike Gleason (http://www.NcFTP.com/contact/).
Connecting to localhost...                                                                                                  
(vsFTPd 2.0.3)
Logging in...                                                                                                               
Login successful.
Logged in to localhost.                                                                                                     
ncftp /home/widemail > bookmarks

* ncftpget
# ncftpget [flags] 원격서버 로컬디렉토리 원격서버디렉토리
# ncftpget -f login.cfg [flags] 로컬디렉토리 원격서버디렉토리
# ncftpget -u 사용자명 -p 패스워드 원격서버 로컬디렉토리 원격서버디렉토리
  -u XX : 사용자명(익명 anonymouss계정을 대신하게됨)
  -p XX : 지정된 사용자의 패스워드
  -P XX : FTP서비스포트 21번 대신 다른 포트번호 사용시 포트번호 지정
  -a    : ASCII 전송모드 지정(지정하지 않으면 BINARY가 기본 전송모드임)
  -t XX : 타임아웃(전송종료)할 초단위 시간지정
  -f XX : 호스트명, 사용자명, 패스워드가 지정된 설정파일
  -A    : 로컬파일에 덮어쓰기하지 않고 추가저장하기
  -F    : PASSIVE 모드 사용(기본값)
  -DD   : 파일 수신 후에 원격서버의 파일 삭제하기
  -R    : 지정한 원격디렉토리의 서브디렉토들까지 통째로 전송하기
- 익명계정으로 원격서버에 접속하여 특정 파일을 가져오기
# ncftpget ftp://ftp.superuser.co.kr/tools/lib.tar.gz
- 익명계정으로 원격서버에 접속하여 지정한 두개 이상의 파일을 한번에 가져오기
# ncftpget ftp://ftp.superuser.co.kr/ . /tools/lib.tar.gz /utility/rsync/rsync-2.5.5.tar.gz
- 익명계정으로 원격서버에 접속하여 지정한 모든 파일을 한번에 가져오기
# ncftpget ftp://ftp.superuser.co.kr/ /tmp/tools/ /tools/*.tar.gz
- 익명계정으로 원격서버에 접속하여 특정 디렉토리의 내용을 통째로 가져오기
# ncftpget -R ftp://ftp.superuser.co.kr/ /root/ /tools/
- 특정계정으로 원격서버에 접속하여 지정한 파일 가져오기
# ncftpget -u dhan ftp://ftp.mycom.net/ . /home/dhan/www/domain.tar.gz
# ncftpget -u dhan ftp://ftp.mycom.net/ /tmp /home/dhan/www/domain.tar.gz
- 특정 계정으로 원격서버에 접속하여 지정한 디렉토리의 모든 내용 통째로 가져오기
# ncftpget -u dhan -R ftp://ftp.mycom.net/ /tmp /home/dhan/www/
- 특정 계정가 패스워드를 지정하여 원격서버에 접속하여 지정한 파일 가져오기
# ncftpget -u dhan -p 12345 ftp://ftp.mycom.net/ . /home/dhan/www/
# ncftpget -u dhan -p 12345 ftp://ftp.mycom.net/ /tmp/ /home/dhan/www/
- 로그인정보파일을 이용하여 원격서버에 접속하여 지정한 파일 가져오기
# cat ncftplogin.cfg
host 192.168.0.100
user dhan
pass 12345
# ncftpget -f ncftplogin.cfg ftp://ftp.mycom.net/ /tmp/ /home/dhan/www/index.html

* ncftpput
# ncftpput [flags] 원격서버 원격서버디렉토리위치 로컬파일
# ncftp -f login.cfg [flags] 서버디렉토리위치 로컬파일
  -u XX : 사용자명(익명 anonymouss계정을 대신하게됨)
  -p XX : 지정된 사용자의 패스워드
  -P XX : FTP서비스포트 21번 대신 다른 포트번호 사용시 포트번호 지정
  -a    : ASCII 전송모드 지정(지정하지 않으면 BINARY가 기본 전송모드임)
  -t XX : 타임아웃(전송종료)할 초단위 시간지정
  -f XX : 호스트명, 사용자명, 패스워드가 지정된 설정파일
  -A    : 로컬파일에 덮어쓰기하지 않고 추가저장하기
  -F    : PASSIVE 모드 사용(기본값)
  -DD   : 파일 수신 후에 원격서버의 파일 삭제하기
  -R    : 지정한 원격디렉토리의 서브디렉토들까지 통째로 전송하기
- 익명계정으로 원격서버에 파일 업로드하기
# ncftpput 192.168.0.00 /pub/ zlib.tar.gz
- 특정계정명으로 원격서버에 파일 업로드하기
# ncftpput -u dhan 192.168.0.00 /home/dhan/ zlib.tar.gz
- 특정 계정명과 패스워드를 지정하여 원격서버에 파일 자동업로드하기
# ncftpput -u dhan -p 12345 192.168.0.00 /home/dhan/ zlib.tar.gz
- 로그인정보파일을 사용하여 원격서버에 자동으로 파일업로드하기
# cat ncftplogin.cfg
host 192.168.0.100
user dhan
pass 12345
# ncftpput -f ncftplogin.cfg /home/dhan/ domain.tar.gz
- 지정한 디렉토리의 모든 파일을 통째로 원격서버에 자동으로 업로드하기
# ncftpput -R -f ncftplogin.cfg /home/dhan/ www

* ncftp배치작업을 위한 ncftpbatch
# ncftpbatch -d
# ncftpbatch -l
# ncftpbatch -D
  -d : 개인 홈디렉토리의 $HOME/.ncftp/spool/디렉토리에 현재 FTP작업들을 백그라운드로 실행한다.
  -l : 계정사용자아ㅢ FTP배치작업큐의 작업리스트를 출력한다.
  -D : -d옵션과 같지만 데몬형식으로 실행되지는 않는다.
- ncftpget을 이용하여 ncftpbatch 작업큐에 백그라운드로 작업하도록 설정
# ncftpget -b ftp://ftp.superuser.co.kr/ . /tools/zlib.tar.gz
- ncftpput을 이용하여 ncftpbatch 작업큐에 백그라운드로 작업하도록 설정
# ncftpput -b 192.168.0.100 /pub/ ./zlib.tar.gz

***********************************************************
* APM
***********************************************************

* zlib 설치
zlib은 gzip등으로 압축된 압축파일을 읽고 쓰기 위해 꼭 필요한 라이브러리이다.
# wget ftp://ftp.superuser.co.kr/etc/zlib-1.1.4.tar.gz
# tar xfz zlib-1.1.4.tar.gz
# cd zlib-1.1.4
# ./configure
# make
# make install

* libpng 설치
zlib가 생성한 이미지파일을 png포맷으로 변형하여 사용하기 위한 라이브러리이다.
# wget ftp://ftp.superuser.co.kr/etc/libpng-1.2.5.tar.gz
# tar xfz libpng-1.2.5.tar.gz
# cd libpng-1.2.5
# cp scripts/makefile.linux makefile
# make test
# make install

* freetype 설치
freetype은 일종의 폰트엔진으로서 작고, 효율적이고, 이미지 출력시 커스트마이징이 쉬운 폰트라이브러리이다.
freetype은 그래픽라이브러리에 사용될 수 있으며 폰트컨버전 툴에서도 사용될수 있다.
또한 텍스트이미지 생성도구도로 많이 사용하고 있다.
# wget ftp://ftp.superuser.co.kr/etc/freetype-2.1.5.tar.gz
# tar xfz freetype-2.1.5.tar.gz
# cd freetype-2.1.5
# ./configure
# make
# make install

* jpeg 설치
JPEG 압축/압축해제 라이브러리 소프트웨어이다.
jpeg이미지 파일을 사용하고 처리하기 위하여 필요한 소프트웨어이다.
# wget ftp://ftp.superuser.co.kr/etc/jpegsrc.v6b.tar.gz
# tar xfz jpegsrc.v6b.tar.gz
# cd jpegsrc.v6b
# ./configure --enable-shared --enable-static
  "--enable-shared"는 GNU libtool을 이용한 공유라이브러리를 생성하기 위한 옵션이며
  "--enable-static"은 GNU libtool을 이용한 정적라이브러리를 생성하기 위한 옵션이다.
# make
# make install

* gd 설치
동적이미지 생성 ANSI C라이브러리로서 PNG, JPEG, GIF의 포맷으로된 이미지들을 생성할 수 있는 유용한 툴이다.
# wget ftp://ftp.superuser.co.kr/etc/gd-2.0.33.tar.gz
# tar xfz gd-2.0.33.tar.gz
# cd gd-2.0.33
# ./configure
  "--with-png=DIR" png관련 라이브러리가 설치된 디렉토리를 지정한다.
  "--with-freetype=DIR" freetype 2.X버전을 지원하기 위한 옵션이다.
  "--with-jpeg=DIR" jpeg라이브러리를 지원하기 위한 옵션이다.
  "--with-xpm=DIR" xpm라이브러리를 지원하기 위한 옵션이다.
# make
# make install

* libxml2 설치
libxml2는 XML C파서(parser)로서 리눅스의 Gnome프로젝트를 위한 툴킷되는 도구이다.
# wget ftp://ftp.superuser.co.kr/etc/libxml2-2.6.16.tar.gz
# tar xfz libxml2-2.6.16.tar.gz
# cd libxml2-2.6.16
# ./configure
# make
# make install

* MYSQL 설치
# wget ftp://ftp.superuser.co.kr/mysql/mysql-4.1.8.tar.gz
# tar xfz mysql-4.1.8.tar.gz
# cd mysql-4.1.8
# ./configure --prefix=/usr/local/mysql --localstatedir=/usr/local/mysql/data --with-charser=euckr
  "--prefix=/usr/local/mysql" 설치될 MYSQL의 위치이다.
  "--localstatedir=/usr/local/mysql/data" MYSQL의 데이터베이스 데이터들이 저장될 위치이다.
  "--with-charset=euckr" MYSQL에서 한글지원을 하기 위한 설정이다.
  "--without-innodb" InnoDB테이블 핸들러를 포함하지 않는다.
  "--disable-largefile" 큰파일 지원을 하지않도록 설정한다. 성능보다는 안정성을 위한 옵션이다.
  "--with-raid" RAID를 지원하도록 설정한다.
  "--with-unix-socket-path=SOCKET" MYSQL소켓파일(mysql.sock)파일의 위치를 지정한다. 단, 반드시 절대경로를 이용해야한다.
  "--with-mysqld-user=username" mysqld데몬을 실행할 계정명을 지정한다.
  "--with-zlib-dir=DIR" 데이터압축 라이브러리인 zlib을 지원하기 위한 zlib설치위치를 지정한다.
  "--with-openssl-includes=DIR" OpenSSL을 지원하기 위한 옵션으로 DIR에는 OpenSSL의 헤더 위치를 지정한다.
  "--with-openssl-libs=DIR" OpenSSL을 지원하기 위한 옵션으로 DIR에는 OpenSSL의 라이브러리 위치를 지정한다.
  "--WITH-ISAM" ISAM타입의 테이블을 지원한다.
# make
# make install
# cd /usr/local/mysql/bin
# ./mysql_install_db
# useradd -M mysql
# chown -R mysql:mysql /usr/local/mysql/data
# /usr/local/mysql/bin/mysqld_safe &
# cd /usr/local/mysql/bin
# mysqladmin -u root password 12345

* APACHE2 설치
# wget ftp://ftp.superuser.co.kr/apache/httpd-2.0.52.tar.gz
# tar xfz httpd-2.0.52.tar.gz
# cd httpd-2.0.52
# vi server/mpm/prefork/prefork.c
  "DEFAULT_SERVER_LIMIT" 값을 1280으로 설정한다. (최대 접속자수를 늘리기 위한 것이다.)
# vi seerver/mpm/worker/worker.c
  "DEFAULT_SERVER_LIMIT"값을 20으로 수정한다. (최대 접속자수를 늘리기 위한 것이다.)
# ./configure --prefix=/usr/local/apache2 --enable-so --with-mpm=worker
  "--prefix=/usr/local/apache2" 설치될 APACHE2의 위치이다.
  "--enable-so" DSO방식으로 설치하기 위한 옵션이다.
  "--with-mpm=worker" 아파치 프로세스가 사용할 멀티스레드방식을 지정한 것으로 worker방식을 지정한 것이다. prefor방식은 전통적인 아파이프로세스 방식이며 worker방식은 스레드 방식을 의미하는 것으로 멀티르로세싱 모듈, 멀티스레드, 멀티르로세서 지원이 가능하다.
  "--disable-access" 호스트기반의 접근제어를 위한 옵션이다.
  "--disable-auth" 사용자기반의 접근제어를 위한 옵션이다.
  "--enable-auth-anon" 익명(anonymous)사용자 액세스를 위한 옵션이다.
  "--enable-auth-dbm" 암호인증방식으로 DBM 데이터베이스의 인증방식을 지원한다.
  "--enable-auth-digest" 암호인증방식으로 RFC2617에 따른 Digest 인증방식을 지원한다.
  "--enable-ssl" SSL/TLS을 지원하다.
  "--disable-status" 아파치 르로세스/스레드 모니터링을 지원하지 않도록 한다.
  "--disable-autoindex" 디렉토리 리스팅을 지원한다.
  "--enable-speling" 보편적으로 알려진 URL가운데 틀린 URL 스펠링을 고쳐주는 스펠링보정긴으을 위한 옵션이다.
  "--with-port=PORT" 아파치 기본포트로 사용할 포트번호를 지정한다.
  "--enable-disk-cache" 디스크캐싱기능을 위한 모듈을 넣을 수 있다.
  "--enable-mem-cache" 메모리 캐싱기능을 위한 모듈을 넣을 수 있다.
  "--enable-auth-ldap" LDAP기반 인증을 지원하기 위한 옵션이다.
# make
# make install
# vi /usr/local/apache2/conf/httpd.conf
  php모듈을 로드하기 위하여 다음가 같이 LoadModuel행을 설정한다.
  LoadModule php4_module libexec/libphp4.so
  LoadModule php5_module libexec/libphp5.so
  확장자가 .php, .html인 파일을 아파치가 PHP로 파상하기 위한 설정이다.
  AddType application/x-httpd-php .php .html
  AddType application/x-httpd-php-source .phps
  한글문자를 기본지원하기 위한 코드설정이다.
  #AddDefaultCharset ISO-8859-1
  AddDefaultCharset EUC-KR
  아파치 웹로그파일(access_log)파일에 이미지에 관한 로그내역을 기록하지 않기 위한 설정이다.
  SetEnvIfNoCase Request_URI (gif|png|jpg|css|js|bmp|jpeg|swf)$ IMAGE=1
  CustomLog logs/access_log common env=!IMAGE
# /usr/local/apache2/bin/apachectl start

* PHP 설치
# wget ftp://ftp.superuser.co.kr/php/php-4.3.9.tar.gz
# tar xfz php-4.3.9.tar.gz
# cd php-4.3.9
# ./configure --with-apxs=/usr/local/apache2/bin/apxs --with-mysql=/usr/loca/mysql --with-freetype-dir --with-zlib-dir --with-jpeg-dir --with-gd --disable-debug --prefix=/usr/local/php
  "--with-apxs=/usr/local/apache2/bin/apxs" 공유된 APACHE2모듈을 함께 연동하기 위한 컴파일 옵션이다.
  "--with-mysql=/usr/local/mysql" 설치할 MYSQL과 PHP를 연동하기 위한 옵션이다.
  "--with-freetype-dir' FreeType2를 지원하기 위한 옵션이다.
  "--with-zlib-dir" PHP에서 zlib을 지원하기 위한 옵션이다.
  "--with-jpeg-dir" PHP에서 jpeg를 지원하기 위한 옵션이다.
  "--with-gd" PHP에서 GD라이브러리관련 함수를 사용하여 GD를 지원하기 위한 옵션이다. (예: --with-gd=/usr/local, --with-gd=/usr/local/gd)
  "--disable-debug" PHP ZendOptimizer를 사용하기 위해서는 debugging을 사용하지 않는다.
  "--with-config-file-path=/usr/local/lib" PHP설정파일 php.ini파일이 존재할 위치를 지정한 것이다.
  "--enable-ftp" PHP에서 FTP관련 함수들을 지원하기 위한 옵션이다.
  "--enable-socket" 소켓(socket)파일을 사용하기 위한 옵션이다.
# make
# make install
# cp php.ini-dist /usr/local/lib/php.ini

* PHP ZendOptimizer 설치
# wget ftp://ftp.superuser.co.kr/ZendOptimizer/ZendOptimizer-2.5.2-Linux_glibc21-i386.tar.gz
# tar xfz ZendOptimizer-2.5.2-Linux_glibc21-i386.tar.gz
# cd ZendOptimizer-2.5.2-Linux_glibc21-i386
# ./install.sh

* php.ini 파일의 설정값 수정
- register_globals 값 수정
- default_sockeet_timeout 값 수정
- post_max_size 값 수정
- memory_limit 값 수정
- upload_max_filesize 값 수정
- allow_url_fopen 값 수정
  이 지시자의 값을 가능하면 off로 설정하기 바란다.
  최근에 웨을 통한 해킹기법에서 이 값이 on으로 서정되어 있기 때문에 문제가 되곤 하였다.

***********************************************************
* APACHE2
***********************************************************

* 아파치를 설치하고 관리하는 관리자는 아래와 같은 정보습득과 역할을 해야만 한다.
- 아파치에 대한 최신정보를 빠르게 얻을 수 있는 곳
- 아파치를 설치하는 방법
- 아파치를 업그레이드하는 방법
- 서버(운영체제)와 아파치와의 상호 관계 분석
- 여러 개의 웹사이트 운용을 위한 가상호스트 설정 방법
- 가입자별 홈디렉토리 셋팅 방법
- 가입자별 사용량 제한 및 체크하기(quota)
- 가입자의 메일 셋팅하기
- 웹로그 분석을 위한 가입자별 디렉토리 설정하기
- DNS 셋팅하기
- 데이터베이스 관리자로서의 역할 수행
- 웹사이트 기획 및 분석 능력
- 웹서버 모니터링하는 방법
- 웹서버의 로그 접속통계분석 하는 방법

* APACHE 디렉토리 구성과 관련파일
/usr/local/apache2 : 아파치 홈디렉토리
/usr/local/apache2/bin : 아파치웹서버 운용에 필요한 여러가지 실행파일들이 있다.
    ab : 아파치 밴치마킹툴
    apachectl : 아파치웹서버의 시작/종료/재시작과 설정파일 검사등을 하는 툴.
    htpasswd : 특정페이지 암호인증에서 ID와 함호를 생성하는 툴.
    htdigest : 사용자 인증파일을 생성하고 업데이트하기 위한 툴.
    httpd: 아파치 웹데몬 파일
    logresolve : 아파치 로그파일에서 IP주소에 대한 호스트네임을 리졸빙하는 툴
    rotatelogs : 아파치 웹로그를 로테이트하기 위한 툴
/usr/local/apache2/logs : 아파치 웹로그파일들을 저장한다.
    access_log : 홈페이지 방문자들의 방문기록을 하는 웹로그파일
    error_log : 홈페이지 에러발생시에 기록하는 에러로그파일
    httpd.pid : 아파치 프로세스의 PID를 기록하는 파일
/usr/local/apache2/conf : 설정파일들이 존재하는 디렉토리
    httpd.conf : 아파치웹서버의 주설정파일
    mime.types : 아파치웹서버의 MIME 타입설정 파일
    ssl.conf : SSL 사용을 위한 설정파일
/usr/local/apache2/htdocs : 기본 홈페이지 문서들이 저장되어 있는 디렉토리
/usr/local/apache2/cgi-bin : CGI파일들이 존재하는 디렉토리. 모든 사용자들이 공통적으로 사용할 CIG파일들을 보관한다.
/usr/local/apache2/error : 에러문서를 저장하고 있는 디렉토리로서 에러코드별로 별도의 파일들이 지정되어 있다.
/usr/local/apache2/icons : 기본적으로 사용하고 있는 이미지아이콘파일들을 저장하고 있다.
/usr/local/apache2/include : 아파치 웹서버에 필요한 C헤더파일들일 존재하는 위치이다.
/usr/local/apache2/lib : 아파치웹서버에서 사용하는 여러가지 라이브러리파일들이 존재하는 위치이다.
/usr/local/apache2/build : 아파치 개발에 관련된 라이브러리 및 도구들
/usr/local/apache2/man : 아파치웹서버으 man페이지 파일들이 존재하는 위치이다.
/usr/local/apache2/manual : 아파치웹서버의 매뉴얼파일이 존재하는 위치이다.
/usr/local/apache2/modules : 아파치에서 사용할 각종 모듈파일들을 직접 컴파일하거나 또는 생성된 모듈파일을 직접 가져와서 이 디렉토리에 저장해 두시고 아파치 httpd.conf 파일에서 LoadModule이라는 구문으로 불러서 사용한다.

* apachectl 관리
start : 아파치를 시작한다.
stop : 아파치실행을 종료한다.
restart : 아파치를 재시작한다.
fullstatus : 아파치의 실행상태를 자세하게 보여준다. (lynx와 mod_status가 설치되어 있어야 한다.)
status : 아파치의 실행상태를 간단하게 보여준다. (lynx와 mod_status가 설치되어 있어야 한다.)
graceful : 아파치를 graceful모드로 재시작한다.
configtest : 아파치 설정파일의 문법을 검사한다.
help : 도움말을 보여준다.







***********************************************************
* 메일 서버
***********************************************************

* sendmail 관련파일들 및 디렉토리
/usr/sbin/sendmail  sendmail 데몬
/usr/bin/makemap  sendmail 맵생성파일(access, virtuser 등 등록시)
/usr/bin/newaliases  앨리어스(alias)파일의 DB를 생성함
/usr/lib/sendmail  sendmail 데몬(대부분 /usr/sbin/sendmail과 링크)
/var/spool/mqueue  sendmail 큐디렉토리(메일 일시저장하는 디렉토리)
/var/spool/mail   받는 메일저장 디렉토리
/etc/mail/access  Relay 제한 및 설정파일
/etc/mail/aliases  앨리어스 설정파일
/etc/mail/domaintable  도메인테이블 등록파일
/etc/mail/local-host-names 최종수진지 설정파일
/etc/mail/sendmail.cf  sendmail 설정파일
/etc/mail/sendmail.cw  최종수신지 설정파일(구버전)
/etc/mail/virtusertable  가상메일주소 등록파일
/etc/init.d/sendmail  sendmail 시작 및 종료 스크립트(rpm 설치시)

* Sendmail 설치
1. 현재 사용중인 sendmail 데몬 확인
2. 현재 사용중인 sendmail 데몬 버전 확인
3. 현재 실행중인 sendmail 실행 중지
# /etc/init.d/sendmail stop
# service sendmail stop
4. 현재 버전 sendmail 데몬 파일 백업하기
# mv /usr/sbin/sendmail /usr/sbin/sendmail.org
5. 현재 버전 sendmail.cf 파일 백업하기
# mv /etc/mail/sendmail.cf /etc/mail/sendmail.cf.org
6. sendmail 최신버전 다운로드하기
# wget ftp://ftp.sendmail.org/pub/sendmail/sendmail-current.tar.gz -> 현재 sendmail.8.13.6.tar.gz
7. 압축풀기
# tar xfz sendmail-current.tar.gz
8. 디렉토리 이동하기
# cd sendmail-current
9. 컴파일
# sh Build
10. mail install 하기
# cd makemap
# make install
11. 에러발생으로 조치후 다시 make install 하기
# mkdir /usr/man
# mkdir /usr/man/man8
12. 새로 생성한 sendmail 데몬파일 복사하기
# cp obj.Linux.2.6.15-1.1833_FC4.i686/sendmail/sendmail /usr/sbin/sendmail
13. sendmail 데몬 파일 소유자(그룹)권한 및 퍼미션 설정하기
# chown root:smmsp /usr/sbin/sendmail
# chmod 2755 /usr/sbin/sendmail
14. 새로 생성한 sendmail.cf 파일 복사
# cp cf/cf/generic-linux.cf /etc/mail/sendmail.cf
15. 새로 설치한 sendmail 시작하기
# /etc/init.d/sendmail start
16. 새로 설치한 sendmail 버전 데몬 확인하기

* sendmail 시작/종료/재시작
- 시작
# /etc/init.d/sendmail start
# service sendmail start
- 재시작
# /etc/init.d/sendmail restart
# service sendmail restart
- 종료
# /etc/init.d/sendmail stop
# service sendmail stop

* /etc/mail/local-host-names (최종수신지 도메인 설정)
sendmail은 이 파일(local-host-names)을 참조하여 현재 서버에 도착한 메일의 최종 수신지라고 인식하게 된다.
이 서버를 거쳐가는 메일 중 이 파일에 등록되어 있는 도메인에 대한 메일을 더 이상 메일라우팅을 시켜 다른 서버로 보내지 않고 이 서버에 잡아두게 된다.

* /etc/mail/access를 이용한 spam 메일 방지법
특정 IP주소 또는 도메인 또는 Email주소, 그리고 특정 네트워크에 대하여 sendmail에 접근하지 못하도록 설정할 수 있는 파일이다.
# cat /etc/mail/access
localhost.localdomain  RELAY
localhost   RELAY
127.0.0.1   RELAY
hung.co.k   RELAY
spam.com   REJECT
spamuser@spam.com  DISCARD
192.168.2.1   REJECT
192.168.3.   REJECT
192.168.4.0/255.255.255.0 REJECT
- RELAY : 메일 RELAY를 허용한다. 즉, 관련(host에서 지정된)메일의 수신/발신을 허용한다.
- REJECT : RELAY와는 반대로 메일 RELAY(수발신)을 허용하지 않는다. 즉, 관련(host에서 지정된) 메일의 수신/발신을 거부한다.
- DISCARD : 메일을 받은후에 폐기한다. 단, 메일발신자에게 폐기통보를 하지 않는다. 즉, /etc/sendmail.cf에 지정된 $#discardmailer에 지정된 곳으로 메일을 폐기함.
- OK : 조건없이 허용한다.
- "501 메시지" : 메일주소가 일부분이상 일치할 경우에 지정된 "메시지"로 거부한다.
- "502 메시지" : 발신메일주소에 host명이 없을 경우에 메일을 받지 않는다.
- "503 메시지" : 관련(host에서 지정된)된 도메인과 관련된 메일을 받지 않는다.
- "550 메시지" : 특정한 도메인에 대해 지정된 "메시지"로 거부한다.
- "571 메시지" : 주로 스팸메일의 경우에 사용하는 설정으로 지정된 "메시지"로 경로메일을 보낸 후에 거부한다.
- "572 메시지" : 571 설정과 거의 유사한 설정이다.
# makemap hash /etc/mail/access.db < /etc/mail/access
or
# cd /etc/mail; make

* 가상메일 설정법
가상도메인으로 사용할 메일주소(예: webmaster)를  /etc/mail/virtusertable에 다음과 같이 등록한다.
가상메일주소와 실제계정사이는 반드시 TAB으로 띄워야한다.
# cat /etc/mail/virtusertable
webmaster@manualand.co.kr manual
webmaster@yung.co.kr  hyung
# makemap hash /etc/mail/virtusertable.db < /etc/mail/virtusertable

* /etc/aliases (메일 애리어스)
# cat /etc/aliases
mailer-daemon:  postmaster
postmaster:     root
staff:  admin,kimdj,parksk,sonts
admin:  :include:/home/admin/admin_list
# newaliases or # /sendmail -bi

* /etc/mail/sendmail.cf 파일의 주요 설정
sendmail을 환경설정하기 위해서는 아래의 설정명령어들로 시작한다.
C: 클래스의 정의 설정
D: 매크로의 정의 설정
E: 환경변수의 정의 설정
F: 클래스의 정의 파일 설정
H: 헤드(Header)정의 설정
K: Key File 정의(map 정의) 설정
M: Mailer 정의 설정
O: 옵션 설정
P: Message Precedences 설정
R: 덮어쓰기 설정(Rewrite)
S: Ruleset 설정
T: 사용자 설정
V: 버전
- 최종수신지 및 localhost 설정(/etc/mail/local-host-name)
현재 이 메일서버를 최종수신지로 설정할 도메인을 설정한다.
sendmail 버전 8.9.x 대 버전에서는 /etc/sendmail.cw를 사용한다.
수정 후에는 sendmail을 재시작해야한다.
- 가상메일 설정파일(/etc/mail/virtusertable)
sendmail에서 가상메일을 사용할 수 있도록 /etc/mail/sendmail.cf파일에는 다음과 같은 설정이 있다.
# Virtual user table (maps incoming users)
Kvirtuser hash -o /etc/mail/virtusertable.db
- relay 설정파일 지정(/etc/mail/access)
/etc/mail/sendmail.cf파일내에는 /etc/mail/access파일이 메일 relay를 제어할 수 있도록 하기 위하여 다음과 같은 설정이 있다.
# Access list database (for spam stomping)
Kaccess hash -T<TMPF> -o /etc/mail/access.db
- 메일 장애시 ㅂ내느 사람이름 설정
메일을 거부하였거나 메일시스템에 장애나 에러가 발생했을 경우에 sendmail은 다음에 설정된 이름으로 리턴메일을 발송한다.
# my name for error messages
DnMAILER-DAEMON
- sendmail 버전 설정
/etc/mail/sendmail.cf파일내에는 DZ라는 지시자 다음에 sendmail의 버전을 표시하고 있다.
# Configuration version number
DZ8.13.4
여기서 설정된 sendmail 버전은 SmtpGreetingMessage 설정에서 $Z의 값으로 사용된다.
- 메일별칭 파일 설정(/etc/aliases)
# location of alias file
O AliasFile=/etc/aliases
- 발송메일 최대크기 제한설정(MaxMessageSize)
발송메일의 최대크기를 제한한다. 단위는 byte이다.
# maximum message size
#O MaxMessageSize=0
- 보관가능한 메일의 최대 크기 설정
MDA가 한번에 받는 메일의 용량을 제한한다.
예를 들어 bible이라는 사용자에게 도착한 메일이 있을 경우에 MDA가 이 메일을 받아서 /var/spool/mail/bible파일에 저장한다.
이 때 받은 메일 한건 한건에 대하여 MDA가 /var/spool/mail/bible파일에 저장하는 한건 한건 용량을 제한하는 설정이다.
주의할 것은 /var/spool/mail/bible파일의 전체 용량을 제한하는 설정이 아니다.
Mlocal,  P=/usr/bin/procmail, F=lsDFMAw5:/|@qSPfhn9, S=EnvFromL/HdrFromL, R=EnvToL/HdrToL, M=2048000
  T=DNS/RFC822/X-Unix,
  A=procmail -t -Y -a $h -d $u
위에서 보는 "M=2048000"은 기본설정에는 없는 부분이다.
이 부분을 추가하면 MDA(예: procmail)가 한건 한건씩을 /var/spool/mail/ID파일에 한건 한건씩 저장할 때의 용량을 2MB로 제한한다.
- sendmail을 백그라운드로 실행
다음은 sendmail데몬을 백그라운드로 실행되도록 설정한 내용이다.
# default delivery mode
O DeliveryMode=background
  DeliveryMode에 설정가능한 값
  interactivity : mqueue에 있는 메일들을 동기화하는 모드로 메일을 전송한다.
  background : mqueue에 있는 메일들을 비동기화하는 모드로 메일을 전송한다.
  queue : 메일을 수신하여 메일큐(queue)에 저장하도록 한다.
  defer : 메일을 수신하여 최대한 빨리 메일큐(queue)에 저장한다.
- 최대 Hop수 제한 설정(MaxHopCount)
hop수란 몇 개의 메일서버를 거쳤는가를 확인하는 값이다.
이 값의 최대값을 설정하고 이 최대값까지 라우팅을 하였음에도 불구하고 메일이 최종수신지까지 도착하지 못하였다면 그 메일은 루프(loop)메일이거나 존재하지 않는 사용자를 최종수신자로 하는 메일로 인정하게 되어 그 메일을 최조 수신지 서버로 리턴한다.
# maximum hop count
#O MaxHopCount=25
- .forward파일 위치 설정
일반적으로 포워딩은 각 계정의 홈디렉토리에 ".forward"라는 파일을 만들어서 이 파일에 특정 메일주소를 입력두면 그 메일주소로 메일을 다시 포워딩한다.
# Forward file search path
O ForwardPath=$z/.forward.$w:$z/.forward
- 로그기록레벨 설정(LogLevel)
sendmail의 로그파일은 /var/log/maillog 파일이다.
이 로그파일에는 정해진 포맷으로 메일 송수신내역을 기록하고 있다.
# log level
O LogLevel=9
  로그레벨의 의미
  0 : sendmail 작동에 관하여 최소한의 정ㅂ만 기록
  1 : 메일처리에 심각한 에러발생 또는 보안정보를 기록함.
  2 : 메일처리시 네트워크 에러 또는 접근 실패의 경우에 기록함.
  3 : 메일처리시 DNS Lookup실패 및 존재하지 않는 주소, forward 처리에러, timeout 발생 등에 따른 접속실패 등을 기록
  4 : 메일처리시에 tcp_wrapper에 의한 접속거부 발생시에 기록함.
  5 : 수신메일의 레코드를 기록함.
  6 : vrfy명령에 의한 사용자 정보를 위한 접속시에 기록함
  7 : sendmail이 메일수신을 실패하였을 경우에 기록함.
  8 : sendmail이 메일수신을 성공하였을 경우에 기록함.
  9 : sendail이 메일처리시에 시스템 자원부족에 의한 수신실패를 기록함.
  10 : 데이터베이스에서 탐색되는 키값 기록함.
  11 : NIS사용시 에러발생가 프로세스 종료 발생시 기록함.
  12 : sendmail에 접속하였을 경우에 기록함.
  13 : 비사용자, 디렉토리 퍼미션 설정등의 상황에 대한 기록을 함.
  14 : sendmail의 접속거부 상황에 대한 기록을 함
  15 : sendmail에서 발생되는 모든 메일 상황을 기록하며 가장 많은 기록을 남기게 됨.
- 메일큐 디렉토리 설정(QueueDirectory)
# queue directory
O QueueDirectory=/var/spool/mqueue
QueueDirectory라는 지시자에 설정된 디렉토리는 sendmail이 받은 메일과 보내는 메일을 바로 Relay하는 것이 아니라 일단 Queue디렉토리에 보관하였다가 다른 메일서버(SMTP서버)나 계정으로 메일을 보내는 용도로 사용된다.
즉, 메일 일시보관용으로 사용되는 공간이 메일큐디렉토리이며 이 지시자에서 이 디렉토리의 위치를 지정한 것이다.
- sendmail Timeout 값 설정
# timeouts (many of these)
#O Timeout.initial=5m
O Timeout.connect=1m
#O Timeout.aconnect=0s
#O Timeout.iconnect=5m
#O Timeout.helo=5m
#O Timeout.mail=10m
#O Timeout.rcpt=1h
#O Timeout.datainit=5m
#O Timeout.datablock=1h
#O Timeout.datafinal=1h
#O Timeout.rset=5m
#O Timeout.quit=2m
#O Timeout.misc=2m
#O Timeout.command=1h
O Timeout.ident=0
#O Timeout.fileopen=60s
#O Timeout.control=2m
O Timeout.queuereturn=5d
#O Timeout.queuereturn.normal=5d
#O Timeout.queuereturn.urgent=2d
#O Timeout.queuereturn.non-urgent=7d
#O Timeout.queuereturn.dsn=5d
O Timeout.queuewarn=4h
#O Timeout.queuewarn.normal=4h
#O Timeout.queuewarn.urgent=1h
#O Timeout.queuewarn.non-urgent=12h
#O Timeout.queuewarn.dsn=4h
#O Timeout.hoststatus=30m
#O Timeout.resolver.retrans=5s
#O Timeout.resolver.retrans.first=5s
#O Timeout.resolver.retrans.normal=5s
#O Timeout.resolver.retry=4
#O Timeout.resolver.retry.first=4
#O Timeout.resolver.retry.normal=4
#O Timeout.lhlo=2m
#O Timeout.auth=10m
#O Timeout.starttls=1h
Timeout 값은 여러가지가 있으나 일반적으로 설정하여 사용하는 것은 Timeout.connect 와 Timeout.queuereturn 값 그리고 Timeout.queuewarn 이다.
Timeout.connect는 sendmail에 접속한 최대시간을 설정하는 기본값으로 5분이 설정된다.
Timeout.queuereturn 값은 기본이 5일(5d)로 설정되어 있으며 어떤 사유로 인하여 메일을 보내지 못해 보관되는 최대값을 지정한다.
Timeout.queuewarn 값은 어떤 사유로 인하여 메일을 발송하지 못했을 경우에 메일발신자에게 경고메일을 보내게 된다. 초기값은 4h 즉 4시간동안 메일을 최종수신자에게 보내지 못했을 경우에 발신자에게 경고메일을 보낸다.
- sendmail 상태파일 지정(StatusFile)
실행 중인 sendmail의 서비스 상태내역을 기록하는 파일을 지정한다.
# status file
O StatusFile=/var/log/mail/statistics
- Mailer의 UID와 GID 설정
# default UID (can be username or userid:groupid)
O DefaultUser=8:12
- 큐보관 최소시간 설정(MinQueueAge)
sendmail이 어떤 사유로 인하여 메일 발송에 실패했을 경우에 다시 시도를 하게 되는데 이 때 다시 시도하기 위해 최소한 어느 정도의 시간을 보낸 후에 재발송 시도를 할것인가를 설정하는 것이다.
일반적으로 "큐(Queue)에 보관할 최소시간설정"이라고 한다.
# minimum time in queue before retry
#O MinQueueAge=30m
- 메일서비스를 일시정지하고 큐에 보관할 Load Average 값
sendmail은 시스템으 ㅣ상태에 따라서 메일서비스를 일시 정지하거나 메일서비스를 중지할 수 있다.
즉, 시스템의 평균부하율(Load Average)이 8이상이 디었을 때에는 메일을 발송하는 작업을 일시정지하고 모든 발송메일을 큐(/var/spool/mqueue)에 일시 보관하게 된다.
# load average at which we just queue messages
#O QueueLA=8
- 메일서비스를 거부할 Load Average 값
다음 RefuseLA 지시자는 sendmail이 메일서비스를 거부하게 되는 시스템평균 부하율 값이다.
# load average at which we refuse connections
#O RefuseLA=12
만약 위으 ㅣ설정이 적용되었을 때에 ps로 sendmail 데몬상태를 확인해 보면 "rejecting connectin"라는 메시지를 확인할 수 있다.
만약 다시 12이하로 떨어지면 메일발송작업은 하지 않지만 /var/spool/mqueue에 보관하는 작업은 한다.
그리고 다시 8 이하로 떨어지게 되면 메일발송업무를 다시 정상화한다.
- sendmail 환경메시지 설정(SmtpGreetingMessage)
# SMTP initial login message (old $e macro)
O SmtpGreetingMessage=$j Sendmail $v/$Z; $b
- Open Relay 설정
sendmail은 /etc/mail/access 파일을 통해 메일 Relay설정을 한다.
이 설정을 유효하게 하는 설정이 바로 /etc/mail/sendmail.cf내에 있은 아래와 같은 설정이다.
# anything else is bogus
R$*                     $#error $@ 5.7.1 $: "550 Relaying denied"
만약 이 설정에 주석(#)처리를 하게 디면 /etc/mail/access 에서 설정된 메일 Relay설정이 전효 유효하지 않게 되며 또한 자신의 메일서버를 스패메일의 온상지로 만들 수도 있다. 이 설정에 주석처리는 하지 않도록 하자.

* 원격지 Relay 허용 옵션 확인
# vi /etc/mail/sendmail.mc
DAEMON_OPTIONS(`Port=smtp,Addr=127.0.0.1, Name=MTA')dnl
=>
dnl DAEMON_OPTIONS(`Port=smtp,Addr=127.0.0.1, Name=MTA')dnl
# m4 /etc/mail/sendmail.mc > /etc/mail/sendmail.cf
# /etc/init.d/sendmail restart

* 스팸방지를 위한 DRAC 실무
- DRAC의 작동방식
1. 메일사용자가 아웃룩 등에서 메일을 작성하고 메일을 발송한다.
   이 사용자가 IP가 유동IP이든 고정IP이든 상관없다.
2. 발송된 사용자의 메일은 POP3에서 사용자의 ID와 패스워드로 먼저 인증을 받는다.
   그리고 인증에 성공한 메일사용자일 경우에는 사용자의 IP주소를 확인하여 /etc/mail/dracd.db파일에 IP주소를 등록한다.
3. 여기에 등록된 IP주소는 영구적으로 저장되는 것이 아니라 기본적으로 30분동안만 저장되고 이후에는 자동삭제된다. 이 저장시간은 변경가능하다.
4. sendmail은 발송자의 메일을 발송하기 위하여 /etc/mail/dracd.db파일과 /etc/mail/access파일을 모두 확인하여 두 파일 중 하나의 파일에라도 발송허용이 되어있다면 메일을 발송시킨다.
   따라서 사용자의 메일을 정상발송된다.
   이 때 /etc/mail/access.db파일을 먼저 검사하고 그 다음 /etc/mail/dracd.db파일을 검사한다.
5. 즉, /etc/mail/dracd.db파일에 등록된 IP주소보다 /etc/mail/acess.db에 등록된 IP주소가 먼저 검사된다.
- DRAC 설치
DRAC이 정상적으로 작동하기 위해서는 다음과 같은 프로그램들과 작업이 필요하다.
portmap : 포트매퍼. drac이 rpc기반으로 작동되기 때문에 필요함.
qpopper : drac과 qpopper가 연동되도록 qpopper를 설치한다.
sendmail : sendmail.cf파일을 수정한다.
db3 : C를 위한 Berkeley DB 데이터베이스 라이브러리.
db3-devel : Berkeley DB 라이브러리를 위한 개발용 파일들.
drac : DRAC패키지이다. 컴파일하여 설치
# mkdir drac
# cd drac
# wget ftp://ftp.superuser.co.kr/mail/drac/drac.tar.Z
# tar xfz drac.tar.Z
# vi Makefile
INSTALL = /usr/bin/install
DEFS = -DSOCKET_RPC -DFCNTL_LOCK -DGETHOST -DDASH_C
CFLAGS = $(DEFS) -g
LDLIBS = -ldb
TSTLIBS = -L. -ldrac
RPCGENFLAGS = -C -I
manlib = 3
manadm = 8
# make
# make install
# make install-man
- drac과의 연동을 위한 qpopper설치
# wget ftp://ftp.superuser.co.kr/qpopper/qpopper4.0.4.tar.gz
# tar xfz qpopper4.0.4.tar.gz
# cd qpopper4.0.4
# ./configure --enable-specialauth --enable-servermode --enable-shy --enable-drac=/usr/loca/drac
  --enable-specialauth 보안을 위해 아호화하거나 암호화된 shadow패스워드를 사용한다.
  --enable-servermode 일반적으로 사용자가 POP3서버에 접속하면 인증 후에 사용자의 메일박스를 임시파일(/var/spool/mail/사용자명.pop)에 복사하며, 이후 사용자가 접속을 종료하였을 때에 삭제되지 않은 메시지들이 원래의 메일박스로 옮겨지게 된다.
   하지만 ㅁ든 사용자가 접속 중에 모든 편지를 다 읽어서 삭제시키다든지 , 아니면 모든 사용자가 접속 중에 모든 편지를 삭제하지 않고 그대로 놓아 둔다면, 이러한 불필요한 복사작업은 없어질 것이다.
   여기서 지정하는 servermode란 이러한 오버헤드를 줄이기 위하여 메일박스 자체에 락(lock)을 걸고 내용을 검사하고 다시 락(lock)을 푸는 방법을 사용한다.
   일반적으로 사용하면 성능을 조금 노핑ㄹ 수 있으니 사용하기를 권한다.
  --enable-shy  qpopper버전 숨기기 위한 것이다. 보안을 위해 사용할 것을 추천한다.
  --enable-drac=/usr/local/drac
# make
# make install
# cp popper/popper /usr/local/lib
# cat /etc/xinetd.d/pop3
service pop3
{
 disable  = no
 socket_type = stream       
 wait  = no
 user  = root
 server  = /usr/local/lib/popper
 log_on_success += HOST DURATION
 log_on_failure += HOST
}
- drac과 연동하기 위한 sendmail.cf 파일 수정
# vi /etc/mail/sendmail.cf
*** drac setup
KDRAC BTREE /ETC/MAIL/DRAC
SLocal_check_rcpt
##### DRAC SETUP
R$* $: $&{client_addr}
R$+ $: $(drac $1 $: ? R)
R$ $@ ?
R$+ @ $#OK
# /etc/init.d/sendmail restart

* SMTP 인증
/etc/mail/sendmail.mc 파일에서 다음과 같이 편집한다.
# vi /etc/mail/sendmail.mc
dnl TRUST_AUTH_MECH(`EXTERNAL DIGEST-MD5 CRAM-MD5 LOGIN PLAIN')dnl
dnl define(`confAUTH_MECHANISMS', `EXTERNAL GSSAPI DIGEST-MD5 CRAM-MD5 LOGIN PLAIN')dnl
=>
TRUST_AUTH_MECH(`EXTERNAL DIGEST-MD5 CRAM-MD5 LOGIN PLAIN')dnl
define(`confAUTH_MECHANISMS', `EXTERNAL GSSAPI DIGEST-MD5 CRAM-MD5 LOGIN PLAIN')dnl
# m4 /etc/mail/sendmail.mc > /etc/mail/sendmail.cf
# /etc/init.d/sendmail restart

* qpopper 설치
# wget ftp://ftp.superuser.co.kr/qpopper/qpopper4.0.4.tar.gz
# tar xfz qpopper4.0.4.tar.gz
# cd qpopper4.0.4
# ./configure --enable-specialauth --enable-servermode --enable-shy --enable-drac=/usr/loca/drac
  --enable-specialauth  보안을 위해 아호화하거나 암호화된 shadow패스워드를 사용한다.
  --enable-servermode  일반적으로 사용자가 POP3서버에 접속하면 인증 후에 사용자의 메일박스를 임시파일(/var/spool/mail/사용자명.pop)에 복사하며, 이후 사용자가 접속을 종료하였을 때에 삭제되지 않은 메시지들이 원래의 메일박스로 옮겨지게 된다.
    하지만 ㅁ든 사용자가 접속 중에 모든 편지를 다 읽어서 삭제시키다든지 , 아니면 모든 사용자가 접속 중에 모든 편지를 삭제하지 않고 그대로 놓아 둔다면, 이러한 불필요한 복사작업은 없어질 것이다.
    여기서 지정하는 servermode란 이러한 오버헤드를 줄이기 위하여 메일박스 자체에 락(lock)을 걸고 내용을 검사하고 다시 락(lock)을 푸는 방법을 사용한다.
    일반적으로 사용하면 성능을 조금 노핑ㄹ 수 있으니 사용하기를 권한다.
  --enable-shy   qpopper버전 숨기기 위한 것이다. 보안을 위해 사용할 것을 추천한다.
  --enable-auto-delete  메일가져간 후에 가져간 메일 자동삭제
  --enable-auth-file=path 허용할 사용자 설정파일 지정
  --enalbe-nonauth-file=path 허용하지 않을 사용자 설정파일 지정
  --enable-spool-dir=path 스풀디렉토리로 사요할 위치지정
  --with-openssl=path  OpenSSL사용(/usr/local/ssl)
  --enable-drac=/usr/local/drac drac 연동 옵션 지정
# make
# make install
# cp popper/popper /usr/local/lib
# cat /etc/xinetd.d/pop3
service pop3
{
 disable  = no
 socket_type = stream       
 wait  = no
 user  = root
 server  = /usr/local/lib/popper
 log_on_success += HOST DURATION
 log_on_failure += HOST
}

***********************************************************
* DNS 서버
***********************************************************

* 도메인 체계
gTLD : 전세계 누구나 등록 가능한 도메인(.com, .net, .org, .edu 등)
nTLD : 국가코드에 의한 국가별 도메인(.kr, .jp, .tw 등)
iTLD : 국제기구 등에서 사용할 수 있는 도메인(.int)
sTLD : 미국내 특정기관만이 사용가능한 도메인(.gov, .mil)

* BIND 설치
- rpm으로 BIND 설치하기
bind-9.2.4-2.i386.rpm  DNS(도메인 이름 시스템)서버
bind-utils-9.2.4-2.i386.rpm DNS 네임서버 질의 및 관리를 위한 유틸리티들
bind-devel-9.2.4-2.i386.rpm BIND DNS 개발을 위해 필요한 파일들과 라이브러리들이 포함된 패키지
bind-chroot-9.2.4-2.i386.rpm BIND 네임서버의 chroot 기능지원 패키지
bind-libs-9.2.4-2.i386.rpm DNS 패키지에서 사용되는 라이브러리들
- 소스 설치하기
# wget ftp://ftp.superuser.co.kr/DNS/9.3.0/bind-9.3.0.tar.gz
# tar xvf bind-9.3.0.tar.gz
# cd bind-9.3.0
# ./configure
# make
# make install

* 네임서버 구성파일과 디렉토리들
/etc/named.conf   BIND의 부트파일로서 각종 옵션과 각 도메인들에 대한 zone파일정보들을 갖고 있다.
/usr/sbin/named   BIND데몬파일
/etc/init.d/named  BIND의 시작/종료/재시작을 할 수 있는 BIND관리 스크립트
/usr/sbin/named-checkconf named.conf파일을 검사하는 유틸리티
/usr/sbin/named-checkzone one파일 유효성 검사 유틸리티
/usr/sbin/rndc   원격 네임서버를 제어하기 위한 관리용 유틸리티. 즉, 원격지에 있는 네임서버를 reload, stop, status 등을 실행할 수 있도록 한다.
/etc/rndc.conf   rndc설정파일로서 rndckey값을 보관한고 있는 파일이다.
/usr/sbin/nsupdate  로컬 또는 원격서버에 있는 네임서버의 zone파일을 갱신할 수 있는 유틸리티. 즉, 동적 DNS갱신유틸리티라고 하낟.
/etc/host.conf   resolver의 옵션을 가지고 있는 파일. 즉 host피일을 먼저 검색할 것인지 아니면 dns에 의한 쿼리를 먼저 할 것인지의 순서를 정하는 설정이 order옵션으로 설정되어 있다.
/etc/resolv.conf  시스템에서 사용할 네임서버의 주소를 가지고 있다.
/etc/named/   각 도메인들에 대한 실제 정보들을 보유하고 있는 zone파일들이 저장된 위치.
/var/named/named.ca  hints파일로서 named의 캐시초기화에 필요한 정보를 저장하고 있는 파일이다.
/var/named/named.local  IP주소를 도메인으로 변경해주는 reverse파일과 비슷한 역할을 하는 파일.
/etc/nsswitch.conf  resolver의 우선순위를 결정하는 파일
/usr/bin/nslookup  대화형 네임서버의 질의 도구
/usr/bin/host   DNS lookup 유틸리티
/usr/bin/dig   host와 같은 DNS lookup 유틸리티

* named 시작, 종료 그리고 재시작
# /etc/init.d/named start
# /etc/init.d/named restart
# /etc/init.d/named stop
# service named start
# service named restart
# service named stop

* named의 보안을 위한 인증키(rndckey) 설정
- /etc/rndc.key 파일
이 파일은 rndckey를 보관하고 있는 키파일로서 /etc/named.conf파이과 /etc/rndc.conf파일에서 include하여 사용한다.
# cat /etc/rndc.key
key "rndckey" {
 algorithm hmac-md5;
 secret "2Tkl86mizSIjhPTkpozyN2Ub0nbFWZQV7B5FPaEVoSxJLjsGkOwNpw9AqIPb";
};
# grep rndc.key /etc/named.conf
include "/etc/rndc.key"
# grep rndc.key /etc/rndc.conf
include "/etc/rndc.key"
- /etc/rndc.key 파일에 rndckey키값 생성하기
//# dnssec-keygen -a hmac-md5 -b 512 -n ZONE -r /dev/random named
# dnssec-keygen -a hmac-md5 -b 512 -n HOST -r /dev/random named
Knamed.+157+58747
# cat Knamed.+157+58747.key
named. IN KEY 512 3 157 T3kDBMYCC6HvCEiizwH1GdftHCvteEkoMUwOOuoppUYB7e5DlS9yzAgF RAAoWGWQH3+MxJ15UpQbz3fnoDgj6g==
/etc/rndc.key파일내의 secret에 들어가야 하는 rndckey값은 "T3kDBMYCC6HvCEiizwH1GdftHCvteEkoMUwOOuoppUYB7e5DlS9yzAgF RAAoWGWQH3+MxJ15UpQbz3fnoDgj6g==" 이다.

* rndc를 이용한 named 데몬 제어
rndc 사용형식: rndc [-c config] [-s server] [-p port] [-k key-file] [-y key] command
               - reload      : 설정파일과 zone파일을 다시 읽어서 재적용함.
        - reload zone : 특정 zone파일만을 다시 읽어서 재적용함.
        - reconfig    : 새로 추가된 zone파일만 읽어서 설정파일을 재적용함.
        - stats       : named상황파일에 서버상황을 저장함.
        - dumpdb      : 덤프파일(named_dump.db)에 캐시데이터를 덤프함.
        - stop        : 저장되지 않은 값을 저장하고 named를 종료함.
        - halt        : 저장되지 않은 값을 그냥 두고 named를 종료함.
        - flush       : ㅐ시데이터를 디스크에 저장하여 동기화함.
        - status      : named데몬의 상태를 출력함.

* dig를 이용한 도메인설정정보 검색
사용형식: dig @server domain query-type query-class
    query-type
    - a     : 네트워크주소를 의미함
    - any   : 지정한 도메인의 모든 정보를 의미함.
    - mx    : 지정한 도메인의 메일서버 정보를 의미함.
    - ns    : 네임서버를 의미함.
    - soa   : SOA정보를 의미함.
    - hinfo : HINFO레코드에 대한 정보를 의미함
# dig @ns.superuser.co.kr txt chaos version bind
# dig @192.168.0.101 txt chaos version bind
# dig @192.168.0.101 any loveve.net
# dig @192.168.0.101 mx loveve.net
# dig @192.168.0.101 ns loveve.net
# dig @192.168.0.101 a loveve.net

* 도메인 쿼리순서 설정파일 : /etc/host.conf
도메인에 대한 IP주소값을 어디에서 찾을 것인가를 결정하는 파일이 /etc/host.conf 파일이다.
즉, 이 파일의 용도는 특정 도메인에 대한 IP를 찾고자 할 때 어디에서 먼저 찾을 것인가에 대한 순서를 정해 놓은 파일이다.
# cat /etc/host.conf
order hosts,bind
맨 앞의 order는 resolver의 순서를 지정한다는 의미이고, 그 다음에는 아래와 같은 옵션들이 올 수 있다.
- hosts : /etc/hosts파일을 지정한다.
- bind : DNS(named 데몬)을 의미한다.
- nis : NIS(Network Information Service)에 의한 domain query를 의미한다.

* 사용할 네임서버 지정파일 : /etc/resolv.conf
이 파일은 서버가 사용할 네임서버를 지정해둔 파일이다.
# cat /etc/resolv.conf
search supuer.co.kr
nameserver 168.126.63.1
nameserver 168.126.63.2
nameserver 211.220.193.181
  search : 호스트만 지정되었을 때에 "호스트+도메인"으로 사용할 도메인명을 지정해 둔 것이다.
  nameserver : 이 항목의 값은 이 서버에서 사용할 네임서버를 지정해 둔 것이다.

* named 설정파일 : /etc/named.conf
# cat /etc/named.conf
//
// named.conf for Red Hat caching-nameserver
//

options {
        directory "/var/named";
 --> 이 지시자는 DNS의 zone파일들이 어디에 위치하는가를 지정한 것이다.

 dump-file "/var/named/data/cache_dump.db";
        statistics-file "/var/named/data/named_stats.txt";
        /*
         * If there is a firewall between you and nameservers you want
         * to talk to, you might need to uncomment the query-source
         * directive below.  Previous versions of BIND always asked
         * questions using port 53, but BIND 8.1 uses an unprivileged
         * port by default.
         */
         // query-source address * port 53;
};

//
// a caching only nameserver config
//
controls {
        inet 127.0.0.1 allow { localhost; } keys { rndckey; };
};
--> 이 지시자는 로컬서버(localhost, 127.0.0.1)에서 named데몬 컨트롤을 허용하고 그 다음 인증키(rndckey)로서 컨트롤을 허용하겠다라는 설정이다.

zone "." IN {
        type hint;
        file "named.ca";
};
--> 이 설정은 DNS의 루트도메인(.)에 대한 설정을 named.ca라는 파일에 하고 있다라는 설정이다.

zone "localdomain" IN {
        type master;
        file "localdomain.zone";
        allow-update { none; };
};

zone "localhost" IN {
        type master;
        file "localhost.zone";
        allow-update { none; };
};

zone "0.0.127.in-addr.arpa" IN {
        type master;
        file "named.local";
        allow-update { none; };
};

zone "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa" IN {
        type master;
        file "named.ip6.local";
        allow-update { none; };
};

zone "255.in-addr.arpa" IN {
        type master;
        file "named.broadcast";
        allow-update { none; };
};

zone "0.in-addr.arpa" IN {
        type master;
        file "named.zero";
        allow-update { none; };
};

include "/etc/rndc.key";
--> named와 rndc가 사용할 인증키값을 저장하고 있는 파일의 이름으로 /etc/rndc.key파일을 지정한 것이다.

zone "dhan.net" {
 type master;
 file "dhan.net.zone";
};
--> 이 설정은 개별 도메인에 대한 설정이다.

* zone파일의 저장위치 : /var/named 디렉토리
개별도메인에 대한 DNS정보가 설정되어 있는 파일이 zone파일이다.
이들 zone파일은 /etc/named.conf파일의 directory지시자에 설정된 디렉토리에 존재해야한다.

* 루트도메인(.) 설정과 업그레이드
최상위 도메인이 루트도메인이므로 모든 네임서버에는 반드시 루트도메인에 대한 정보를 가지고 있어야 한다.
따라서 BIND를 설치하여 네임서버를 운영하고 있다면 이 루트도메인에 대한 설정을 확인하고 루트도메인 설정파일을 주기적으로 업데이트하는 것이 좋다.
# wget ftp://ftp.rs.internic.net/domain/named.root
# mv named.root /var/named/named.ca

* localhost 도메인 설정파일 : /var/named/named.local
이 파일은 서버 자기자신의 localhost에 대한 설정사항으로 기본값으로 사용토록 한다.

* 네임서버설정파일(named.conf) 문법검사 유틸리티 : named-checkconf
named-checkconf 를 이용하면 /etc/named.conf파일의 문법을 검사하여 어떤 문제점이 있는가를 사전에 알 수가 있다.
사용형식 : named-checkconf [-v] [-t directory] filename

* 특정 zone파일검사 유틸리티 : named-checkzone
/var/named/ 디렉토리에 있는 개별 zone파일들의 오류여부를 점검하는 유틸리티이다.
사용형식 : named-checkzone 도메인명 zone 파일절대경로명
# named-checkzone bible10.co.kr /var/named/bible101.co.kr.zone

* DNS에 새로운 도메인 추가
1 도메인등록대행기관에 도메인을 등록합니다.
2. /etc/named.conf파일에 zone구문 설정
zone "dhan.net" {
 type master;
 file "dhan.net.zone";
};
3. 해당 도메인의 zone파일 생성
# vi /var/named/dhan.net.zone
;-----------------------------------------------------------------
; dhan.net
; created by dhan
;-----------------------------------------------------------------
$TTL 3600
@  IN SOA ns.dhan.net. dhan.dhan.net. (
  2005030401 ; serial number
  28800  ; secondaries refresh every 8 hours
  14400  ; if refresh fails, retry every 4 hours
  2419200  ; if cannot refresh, expire IN 30 days
  86400  ; default ttl
)

;-----------------------------------------------------------------
  IN NS ns.dhan.net.
  IN MX 10 mail.dhan.net.

;-----------------------------------------------------------------
ns  IN A 202.86.12.131
mail  IN A 202.86.12.131
www  IN A 202.86.12.141
4. named데몬 재시작
# /etc/init.d/named restart

* zone파일의 설정법과 해석하기
$TTL : 이 값은 Minimum(TTL, Time to Live)항목에 설정되어 있는 값과 의미가 같은 것이다.
       즉, 도메인에 대한 정보를 다른 네임서버에서 가져간 다음 가져간 네임서버에 얼마나 보관할 것인가를 지정한 것이다.
       도메인 정보를 다른 서버에서 가져간 후에 그 서버의 캐시에 얼마나 보관할 것인가 대한 시간을 초로 설정해 둔 것이다.
@ : zone파일내에서 @는 origin도메인을 의미한다.
    즉, /etc/named.conf파일에 설정되어 있는 도메인명을 의미한다.
SOA : SOA는 "Start Of Authority"의 약어로서 해당 도메인에 대하여 여기서 설정한 네임서버가 모든 정보를 가지고 있음을 선언하는 것으로 해당 도메인에 대한 모든 권한이 여기 있다는 것을 의미한다.
Serial :
Refres :
Retry :
Expire :
Minimum :
NS 레코드 :
A 레코드 :
MX 레코드 :
CNAME 레코드 :
HINFO 레코드 :
UINFO 레코드 :
MINFO 레코드 :
PTR 레코드 :
TXT 레코드 :
WKS 레코드 :





* 특정 도메인에 대한 서브도메인 설정
- 개별 서브도메인들이 모두 동일한 IP주소로 매핑하는 예
 IN A 192.168.0.201
www IN A 192.168.0.201
db IN A 192.168.0.201
ftp IN A 192.168.0.201
- 개별 서브도메인들이 모두 다른 IP주소로 매핑하는 예
 IN A 192.168.0.201
www IN A 192.168.0.202
db IN A 192.168.0.203
ftp IN A 192.168.0.204
- 설정되지않은 모든 서브도메인들을 특정 IP주소로 매핑하는 예
 IN A 192.168.0.201
www IN A 192.168.0.202
db IN A 192.168.0.203
ftp IN A 192.168.0.204
* IN A 192.168.0.205

* DNS로 구현하는 서버 부하분산기능 설정법
$TTL 86400
@  IN SOA ns.dhan.net. dhan.dhan.net. (
  2006030401 ; serial number
  28800  ; secondaries refresh every 8 hours
  14400  ; if refresh fails, retry every 4 hours
  2419200  ; if cannot refresh, expire IN 30 days
  86400  ; default ttl
)
  IN NS 192.168.0.2
;
www 0 IN A 192.168.0.201
www 0 IN A 192.168.0.202
www 0 IN A 192.168.0.203
www 0 IN A 192.168.0.204
www 0 IN A 192.168.0.205
위의 예를 보면 5개의 www행의 캐싱값이 모드 0이다.
이것은 캐싱을 하지 말라는 의미로서 DNS의 Round-Robin기능을 이용할 때에 가능한 개별 레코드의 값은 0으로 설정하여 캐싱되지 않도록 하는 것이 좋다.
그러면 http://www.dhan.net/에 접속할 때마다 DNS에 질의하게 될 것이며 이 때마다 순차대로 응답을 하게 될 것이다.

* Lame Server 관련된 DNS 에러메시지 처리하기
# vi /etc/named.conf
logging {
 category lame-servers { null; };
};
위의 설정은 named 의 로그기록에 관한 설정으로 lame-servers에 관련된 로그를 남기지 않도록(null) 처리한 것이다.
만약 named에 관련된 꼭 필요하지 않은 로그들을 모두 기록하지 않고자 한다면 다음과 같이 내용을 추가한다.
logging {
 category lame-servers { null; };
 category unmatched { null; };
 category network { null; };
 category notify { null; };
};

***********************************************************
* MySQL 서버
***********************************************************

* MySQL 실행과 종료
- MySQL 실행
사용형식 : /usr/local/mysql/bin/mysqld_safe [각종 옵션들] &
- MySQL 종료
사용형식 : /usr/local/mysql/bin/mysqladmin -u root -p shutdown

* MySQL 기본관리
- MySQL 접속
사용형식 : /usr/local/mysql/bin/mysql -u MySQL_계정명 -p 접속할_데이터베이스명 -h 접속할 호스트이름 -P 접속포트번호 -S /tmp/mysql.sock
           mysql                 : MySQL 데이터베이스로 접속하기 위한 MySQL 클라이어트 프로그램
    -u                    : 접속할 MySQL 계정(사용자)명을 지정하기 위한 옵션
    MySQL_계정명          : 데이터베이스로 접속할 MySQL의 사용자(계정)명
    -p                    : 패스워드를 입력하기 위한 옵션(패스워드가 없을 경우에 생략가능함)
    접속할_데이터베이스명 : 접속 후 사요할 데이터베이스명
    -h                    : 접속할 호스트이름
    -P                    : 접속할 포트번호
    -S                    : 접속할 MySQL 소켓파일 위치 지정
# /usr/local/mysql/bin/mysql -u root -p mysql
Enter password: ********
# /usr/local/mysql/bin/mysql -u picasso -p picasso_db
Enter password: ********
# /usr/local/mysql/bin/mysql -u root -p'12345678' mysql
# /usr/local/mysql/bin/mysql -u user -p'11111111' user_db
# /usr/local/mysql/bin/mysql -u ruser -p ruser_db -h 192.168.0.111 -P 3306
Enter password: ********
- 패스워드 재설정
사용형식 : /usr/local/mysql/bin/mysqladmin -u 계정명 password 변경할_패스워드
# /usr/local/mysql/bin/mysqladmin -u root password 12345678 -> 기존에 패스워드가 존재하지 않을 때
Enter password: ********
# /usr/local/mysql/bin/mysqladmin -u root -p password 12345678 -> 기존에 패스워드가 존재할 때
Enter password: ********
# /usr/local/mysql/bin/mysqladmin -u user -p password 11111
Enter password: ********
# /usr/local/mysql/bin -u root -p'12345678' mysql
mysql> update user set password = password('12345678') where user = 'root';
mysql> set password for root = password('2345');
- 쉘상태에서 특정파일에 저장된 SQL문 실행하기
# /usr/local/mysql/bin -u user -p user_db < /home/user/user.sql
Enter password: ********
- 새로운 MySQL 사용자를 위한 MySQL 설정
mysql> create database user_db;
mysql> grant all on user_db.* to user@'localhost' identified by 'password';
## mysql> revoke all on user_db.* from user@'localhost';
## mysql> revoke all on user_db.* from user;
## mysql> delete from mysql.user where user = 'user';

* MySQL 패스워드 복구
1. 실행중인 MySQL을 종료한다.
# ps -ef | grep mysqld
# killall mysqld
2. table grant 권한없이 MySQL을 실행한다.
# /usr/local/mysql/bin/mysqld_safe --skip-grant-table &
3. 패스워드없이 MySQL root 계정으로 MySQL에 접속한다.
# /usr/local/mysql/bin/mysql -u root mysql -> 패스워드 지정 안함
4. UPDATE 문으로 MySQL root사용자의 패스워드를 변경한다.
mysql> update user set password = password('12345678') where user = 'root';
5. "flush privileges"로 MySQL데이터들을 동기화한다.
mysql> flush privileges;
6. MySQL을 빠져나와서 실행했던 MySQL을 종료한다.
# killall mysqld
# /usr/local/mysql/bin/mysqladmin -u root shutdown
7. 일반적인 방법으로 MySQL을 실행한다.
# /usr/local/mysql/bin/mysqld_safe --skip-grant-table &
8. 패스워드를 사용하여 MySQL의 root계정으로 접속하여 확인한다.

* "too many connections" 에러 해결방법
원인: 이에 대한 원인을 설명하기 위해서는 php에서 사용하는 MySQL 연결함수인 mysql_connect()와 mysql_pconnect의 차이점 그리고, mysql_close()함수에 대한 정확한 이해가 필요하다.
      mysql_connect()함수를 이용하여 MySQL에 연결하였다면 해당 스크립트가 종료됨과 동시에 mysql_close()함수를 호출하지 않았다 하더라도 자동으로 연결이 종료된다.
      하지만, mysql_pconnect()함수는 해당 스크립트가 종료된 후 mysql_close()함수가 호출되어도 연결이 끊어지지 않은 채로 계속 연결을 유지하고 있다.
      따라서, 얼핏보기에는 "too many connections"라는 에러메시지는 mysql_pconnect()라는 함수의 사용때문에 발생하는 것 같지만 (물로 그런 이유도 있을 수 있지만) 근본적인 원인은 이와 다를 수 있다.
      결론적으로 "mysqladmin -u -p variables"의 결과로서 볼 수 있었던 MySQL의 환경변수값들 가운데 "wait_timeout"의 값만큼 서버에 그대로 연결을 유지한 채로 남아있는 것이다.
      따라서, 이것이 "too many connections" 에러가 발생하는 긍극적인 원인이 되는 것이다.
      이를 해겨하면 MySQL이 빠른 응답을 할 수 있도록 서버 하드웨어 사양(CPU, 메모리 등)을 높이는 방법도 있겠지만 급하게 조치해야하는 경우에는 MySQL의 필요한 변수의 값을 보다 높게 설정하는 방법이 현명할 것이다.
      "mysqladmin -u root -p variables"라고 하면 MySQL의 전체 환경변수의 값을 확인할 수 있다.
      이들 MySQL 환경변수 가운데 다음 3가지의 값을 현재보다 높게 설정함으로서 "too many connection" 에러를 응급초지할 수 있다.
      - max_connection : MySQL에 connect할 수 있는 최대 개수
      - table_cache    : MySQL의 테이블 캐쉬크기
      - wait_timeout   : MySQL에 connect하여 쿼리를 지속할 수 있는 시간
      # /usr/local/mysql/bin/mysqladmin -u root -p variables | grep max_connections
      | max_connections                 | 100                                   |
      # /usr/local/mysql/bin/mysqladmin -u root -p variables | grep table_cache
      | table_cache                     | 64                                    |
      # /usr/local/mysql/bin/mysqladmin -u root -p variables | grep wait_timeout
      | innodb_lock_wait_timeout        | 50                                    |
      | table_lock_wait_timeout         | 50                                    |
      | wait_timeout                    | 28800                                 |
      # /usr/local/bin/mysqld_safe -O max_connections=500 -O table_cache=256 -O wait_timeout=57600 &

* MySQL 관리자 전용 유틸리티 mysqladmin
사용형식 : /usr/local/mysql/bin/mysqladmin -u root -p 명령어
- 새로운 데이터베이스 생성하기
사용형식 : /usr/local/mysql/bin/mysqladmin -u root -p create 새로운_데이터베이스명
# /usr/local/mysql/bin/mysqladmin -u root -p create user_db2
Enter password: ********
- 특정 데이터베이스 삭제하기
사용형식: /usr/local/mysql/bin/mysqladmin -u root -p drop 삭제할_데이터베이스명
# /usr/local/mysql/bin/mysqladmin -u root -p drop user_db2
Enter password: ********
- MySQL 권한테이블 갱신하기
사용형식 : /usr/local/mysql/bin/mysqladmin -u root -p reload
# /usr/local/mysql/bin/mysqladmin -u root -p reload
Enter password: ********
- MySQL에 접속한 사용자 확인하기
사용형식: /usr/local/mysql/bin/mysqladmin -u root -p processlist
# /usr/local/mysql/bin/mysqladmin -u root -p processlist
- 접속되어 있는 MySQL 사용자(thread) 접속 끊기
사용형식 : /usr/local/mysql/bin/mysqladmin -u root -p kill 쓰레드번호
# /usr/local/mysql/bin/mysqladmin -u root -p processlist -> 종료시킬 쓰레드 확인
# /usr/local/mysql/bin/mysqladmin -u root -p kill 8  -> Id가 8인 쓰레드 종료
- MySQL의 간단한 실행정보 확인하기
사용형식 : /usr/local/mysql/bin/mysqladmin -u root -p status
# /usr/local/mysql/bin/mysqladmin -u root -p status
Uptime: 2095884  Threads: 12  Questions: 2578614  Slow queries: 0  Opens: 0  Flush tables: 1  Open tables: 138  Queries per second avg: 1.230
- MySQL의 현재 상황 자세히 살펴보기
사용형식 : /usr/local/mysql/bin/mysqladmin -u root -p extended-status
# /usr/local/mysql/bin/mysqladmin -u root -p extended-status
- MySQL의 환경변수를 확인하기
사용형식 : /usr/local/mysql/bin/mysqladmin -u root -p variables
# /usr/local/mysql/bin/mysqladmin -u root -p variables
- 현재 MySQL의 정확한 버전과 여러 가지 실행정보 확인하기
사용형식 : /usr/local/mysql/bin/mysqladmin -u root -p version
# /usr/local/mysql/bin/mysqladmin -u root -p version
- MySQL이 죽었는지 살았는지 확인하기
사용형식 : /usr/local/mysql/bin/mysqladmin -u root -p ping
# /usr/local/mysql/bin/mysqladmin -u root -p ping

* MySQL 로그
- 쿼리로그 옵션 형식 : --log=쿼리로그파일명
                       이 형식을 사용하면 MySQL 데이터 디렉토리에 "쿼리로그파일명"으로 지정된 로그파일이 생성되며 SQL 쿼리로그를 기록한다.
- 바이너리로그 옵션 형식 : --log-bin=바이너리로그파일명
                           바이너리로그를 보기위해서는 /usr/local/mysql/bin/mysqlbinlog 명령어를 사용해야 한다.
      이 형식을 사용하면 MySQL 데이터디렉토리에 "바이너리로그파일명"으로 지정된 바이너리로그파일이 생성되면 MySQL데이터의 변경내역을 기록한다.
- SLOW쿼리 옵션 형식 : --log-slow-queries=SLOW쿼리파일명
                       이 형식을 사용하면 MySQL 환경변수 "long_query_time"에 초단위로 지정된 시간보다 초과하는 쿼리문만을 기록한다.

* 텍스트파일의 데이터를 MySQL의 특정 데이터베이스로 입력하기
사용형식 : /usr/local/mysql/bin/mysqlimport -u MySQL사용자 -p 데이터베이스명 텍스트파일명
           이 형식으로 실행하면 "텍스트파일명"에 저장되어 있는 데이터가 "데이터베이스명"의 특정 "테이블"의 데이터로 입력된다.
    위의 형식에서 데이터가 저장될 테이블이름은 "텍스트파일명"과 동일한 테이블이 된다.
    위의 형식에서 "텍스트파일명"은 "테이블명"과 동일한 이름이어야 한다.
    또한 저장될 테이블은 이미 생성되어 있어야 한다.
# cat addressbook
PARKSUNGSOO 011-111-2222 SEOUL A+
JUNGWOOYOUNG 011-222-3333 SEOUL B+
LEEJAESUK 016-222-1111 BUSAN C+
CHOYUEJIN 019-333-4444 BUSAN D+
# /usr/local/mysql/bin/mysqlimport -u root -p user_db2 addressbook
Enter password: ********
user_db2.addressbook: Records: 4 Deleted: 0 Skipped: 0 Warning: 0

* MySQL 데이터베이스 스키마 확인
사용형식 : /usr/local/mysql/bin/mysqlshow [옵션] [데이터베이스 [테이블 [컬럼]]]
           1. 위의 형식에서 특정 데이터베이스명이 주어지지 않는다면 MySQL내에 존재하는 모든 데이터베이스들을 나열한다.
    2. 위의 형식에서 특정 테이블명이 주어지지 않는다면 지정된 데이터베이스내의 모든 테이블들을 나열한다.
    3. 위의 형식에서 특정 컬럼이 주어지지 않는다면 지정된 테이블내에 존재하는 모든 컬럼들과 컬럼타입들을 나열한다.
- MySQL에 존재하는 모든 데이터베이스들 확인하기
# /usr/local/mysql/bin/mysqlshow -u root -p
mysql> show databases;
- 특정 데이터베이스의 테이블들 확인하기
# /usr/local/mysql/bin/mysqlshow -u root -p user_db
mysql> use user_db;
mysql> show tables;
- 특정 테이블의 컬럼정보들 확인하기
# /usr/local/mysql/bin/mysqlshow -u root -p user_db addressbook
mysql> use user_db;
mysql> show columns from addressbook;
mysql> desc addressbook;
- 특정 테이블의 하나의 컬럼만 확인하기
# /usr/local/mysql/bin/mysqlshow -u root -p user_db addressbook name

* MySQL 데이터베이스 데이터 백업과 복구
- /usr/local/mysql 디렉토리 전체를 압축백업하기
# cd /usr/local
# tar cvfpz /backup/mysql.tar.gz mysql
- MySQL 데이터 백업하기
사용형식1 : /usr/local/mysql/bin/mysqldump [옵션] DB [TABLES ..] > 파일명
            DB는 백업대상이 되는 데이터베이스명이고 TABLES는 지정한 백업대상 데이터베이스내에 존재하는 테이블명이다.
     이 백업의 의미는 DB의 데이터베이스내에 존재하는 테이블의 내용을 백업해서 "파일명"에 저장하라는 의미이다.
사용형식2 : /usr/local/mysql/bin/mysqldump [옵션] --databases [옵션] DB1 [DB2 ..] > 파일명
            --databases라는 옵션에 의해 DB1 DB2 의 데이터베이스들을 백업하여 "파일명"에 저장한다.
     즉 백업대상이 되는 데이터베이스가 2개 이상이 될 때에는 --databases 옵션을 사용하고 그 뒤에 백업할 데이터베이스를 지정한다.
사용형식2 : /usr/local/mysql/bin/mysqldump [옵션] --all-databases [옵션] > 파일명
            --all-databases라는 옵션은 MySQL내에 존재하는 모든 데이터베이스를 백어밷상으로 한다는 의미이다.
- 특정 데이터베이스 데이터 백업과 복구
백업형식 : /usr/local/mysql/bin/mysqldump -u DB계정명 -p 백업대상_데이터베이스명 > 저장할_파일명
복구형식 : /usr/local/mysql/bin/mysql -u DB계정명 -p 복구할_데이터베이스명 < 저장한_파일명
# /usr/local/mysql/bin/mysqldump -u root -p mysql > mysql.sql
# /usr/local/mysql/bin/mysql -u root -p mysql < mysql.sql
# /usr/local/mysql/bin/mysqldump -u user -p user_db > user_db.sql
# /usr/local/mysql/bin/mysql -u user -p user_db < user_db.sql
- 특정 데이터베이스의 특정 테이블의 백업과 복구
백업형식 : /usr/local/mysql/bin/mysqldump -u DB계정명 -p 데이터베이스명 테이블명 > 저장할_파일명
복구형식 : /usr/local/mysql/bin/mysql -u DB계정명 -p 데이터베이스명 < 저장한_파일명
# /usr/local/mysql/bin/mysqldump -u user -p user_db addressbook > addressbook.sql
# /usr/local/mysql/bin/mysql -u user -p user_db < addressbook.sql
- 여러 개의 데이터베이스 한번에 백업과 복구
백업형식 : /usr/local/mysql/bin/mysqldump -u DB계정명 -p --databases [옵션] DB1 [DB2 ..] > 저장할_파일명
          CREATE DATABASE문과 USE문이 추가되어 있다.
복구형식 : /usr/local/mysql/bin/mysql -u DB계정명 -p < 저장한_파일명
# /usr/local/mysql/bin/mysqldump -u user -p --databases user_db user_db2 > user_dbs.sql
# /usr/local/mysql/bin/mysql -u user -p < user_dbs.sql
-- MySQL 전체 데이터베이스 백업하기
사용형식 : /usr/local/mysql/bin/mysqldump -u DB계정명 -p --all-databases > 저장할_파일명
# /usr/local/mysql/bin/mysqldump -u root -p --all-databases > all_dbs.sql
- 기존 테이블을 삭제후 백업된 파일로 복구하기 위한 백업방법
  mysqldump문으로 데이터베이스 백업시에 각각의  CRETE TABLE문 앞에 DROP TABLE문 삽입하기
# /usr/local/mysql/bin/mysqldump -u user -p --add-drop-table user_db > user_db.sql
# /usr/local/mysql/bin/mysql -u user -p user_db < user_db.sql
- 데이터베이스 백업시 에러발생율 무시하고 계속 진행하기
사용형식 : /usr/local/mysql/bin/mysqldump -u 사용자명 -p -f DB명 > 파일명
# /usr/local/mysql/bin/mysqldump -u user -p -f user_db > user_db.sql
- 원격서버의 MySQL 데이터베이스 백업하기 #1(기본포트 사용)
사용형식 : /usr/local/mysql/bin/mysqldump -u 사용자명 -p -h 호스트명(IP주소) DB명 > 파일명
# /usr/local/mysql/bin/mysqldump -u user -p -h 192.168.0.111 user_db > user_db.sql
- 원격서버의 MySQL 데이터베이스 백업하기 #2(특정 포트번호 지정)
사용형식 : /usr/local/mysql/bin/mysqldump -u 사용자명 -p -h 호스트명(IP주소) -P 포트번호 DB명 > 파일명
# /usr/local/mysql/bin/mysqldump -u user -p -h 192.168.0.111 -P 22222 user_db > user_db.sql
- 데이터 백업시 CREATE DATABASE 문을 생략하여 백업하기
사용형식 : /usr/local/mysql/bin/mysqldump -u 사용자명 -p -n [옵션] DB명 > 파일명
           /usr/local/mysql/bin/mysqldump -u 사용자명 -p --no-create-db [옵션] DB명 > 파일명
# /usr/local/mysql/bin/mysqldump -u user -p -n --databases user_db user_db2 > user_dbs.sql
# /usr/local/mysql/bin/mysqldump -u root -p -n --all-databases > all_dbs.sql
-- 데이터 백업시에 CREATE TABLE문을 생략하여 백업하기
사용형식 : /usr/local/mysql/bin/mysqldump -u 사용자명 -p -t DB명 > 파일명
           /usr/local/mysql/bin/mysqldump -u 사용자명 -p --no-crate-info DB명 > 파일명
# /usr/local/mysql/bin/mysqldump -u user -p -t user_db > user_db.sql
- 데이터는 백업하지않고 테이블 스키마만 백업하기
사용형식 : /usr/local/mysql/bin/mysqldump -u 사용자명 -p -d DB명 > 파일명
           /usr/local/mysql/bin/mysqldump -u 사용자명 -p --no-data DB명 > 파일명
# /usr/local/mysql/bin/mysqldump -u user -p -d user_db > user_db.sql
-- 특정 데이터베이스의 조건에 맞는 데이터만 백업하기
사용형식 : /usr/local/mysql/bin/mysqldump -u 사용자명 -p -w="WHERE 조건문" DB명 테이블명 > 파일명
           /usr/local/mysql/bin/mysqldump -u 사용자명 -p --where="WHERE 조건문" DB명 테이블명 > 파일명
# /usr/local/mysql/bin/mysqldump -u user -p -w="admin_id = 'admin'" user_db addressbook > admin_id.sql
- select문을 이용한 데이터 백업 및 복구
mysql> select * into outfile './backup.sql' from addressbook;
mysql> load data infile './backup.sql' into table addressbook;

* 데이터베이스의 깨진 테이블파일 복구
- 데이터파일 구성
테이블 하나에 아래의 파일형식을 가진 파일이 3개씩 생성되어 그 테이블의 데이터를 실제로 저장하는 용도로 사용된다.
*.MYD: 해당 테이블의 데이터가 저장되는 테이블 데이터파일이다.
*.MYI: 해당 테이블의 인덱스정보가 저장되는 테이블 인덱스파일이다.
*.frm: 해당 테이블의 테이블구조가 저장되는 테이블 스키마파일이다.
- myisamchk
사용형식 : myisamchk [옵션] 점검복구새상테이블인덱스파일(*.MYI)
           주의할 것은 myisamchk의 복구 대상파일은 반드시 MySQL의 인덱스파일이라는 것이다.
           또한 반드시 MySQL을 종료한 후에 사용하라.
- MySQL 테이블파일의 이상유무 점검
# /usr/local/mysql/bin/myisamchk /usr/local/mysql/var/user_db/addressbook.MYI
# /usr/local/mysql/bin/myisamchk -c /usr/local/mysql/var/user_db/addressbook.MYI
# /usr/local/mysql/bin/myisamchk --check /usr/local/mysql/var/user_db/addressbook.MYI
- MySQL 테이블 점검시 이상발견시만 알려주기
# /usr/local/mysql/bin/myisamchk -s /usr/local/mysql/var/user_db/addressbook.MYI
# /usr/local/mysql/bin/myisamchk --silent /usr/local/mysql/var/user_db/addressbook.MYI
- MySQL의 테이블 점검(복구)시 가능한 상세하게 메시지 출력하기
# /usr/local/mysql/bin/myisamchk -v /usr/local/mysql/var/user_db/addressbook.MYI
# /usr/local/mysql/bin/myisamchk --verbose /usr/local/mysql/var/user_db/addressbook.MYI
- MySQL의 테이블 이상유무 점검시 결과를 상세히 종합하여 보여주기
# /usr/local/mysql/bin/myisamchk -i /usr/local/mysql/var/user_db/addressbook.MYI
# /usr/local/mysql/bin/myisamchk --information /usr/local/mysql/var/user_db/addressbook.MYI
-- MySQL의 특정테이블 이상유무를 가장 정밀하게 점검하기
# /usr/local/mysql/bin/myisamchk -ev /usr/local/mysql/var/user_db/addressbook.MYI
# /usr/local/mysql/bin/myisamchk --extend-check --verbose /usr/local/mysql/var/user_db/addressbook.MYI
- 정형적인 방법으로 MySQL의 깨진 테이블파일 복구하기
# /usr/local/mysql/bin/myisamchk -rv /usr/local/mysql/var/user_db/addressbook.MYI
# /usr/local/mysql/bin/myisamchk --recover --verbose /usr/local/mysql/var/user_db/addressbook.MYI
-- MySQL의 깨진 테이블파일 안전모드로 복구하기
# /usr/local/mysql/bin/myisamchk -o /usr/local/mysql/var/user_db/addressbook.MYI
# /usr/local/mysql/bin/myisamchk --safe-recover /usr/local/mysql/var/user_db/addressbook.MYI
  -r옵션을 사용하여 테이블파일을 복구하는 것은 MySQL의 테이블 복구 방법 가운데 가장 정형적인 방법이다.
  하지만 데이터의 안전성을 고려한다면 -o옵션을 사용하라.
  깨진 테이블파일의 오류를 복구할 때에 시간이 좀 더 소요되더라도 데이터의 안전성이 걱정이 된다면 -o옵션을 사용하라.
  -o옵션을 사용하면 전통적인 방법으로 복구를 하며 -r옵션을 사용하는 것보다 속도는 좀 떨어지지만 -r옵션으로 복구하지 못하는 데이터파일을 복구할 수 있다.
- MySQL 테이블이 완전히 깨졌을 때의 최후의 복구방법
# /usr/local/mysql/bin/myisamchk -re /usr/local/mysql/var/user_db/addressbook.MYI
# /usr/local/mysql/bin/myisamchk -oe /usr/local/mysql/var/user_db/addressbook.MYI
- MySQL 내부명령어 사용
사용형식 : repair table 테이블명;
mysql> repair table addressbook;

***********************************************************
* 리눅스 호스팅서버 관리
***********************************************************

* 호스팅서비스에 필요한 것들
- 리눅스 운영체제
- 아파치 웹서버
- MySQL 데이터베이스
- PHP, Perl 등과 같은 웹프로그래밍언어
- 메일서비스 지원을 위한 Sendmail, Qmail, POP, IMAP
- Webalizer와 accesswatch 등을 이용한 웹로그분석 제공
- Proftpd, vsftpd를 이용한 FTP서비스
- BIND를 이용한 DNS서비스
- 여러가지 라이브러리 및 어플리케이션들
- 백업서버 구축

* 신규 호스팅가입자 서버셋팅
1. 사용자 ID, 패스워드 생성
# useradd bible
# passwd bible
2. 사용자 호스팅용 홈디렉토리 구성하기
# mkdir /home/bible/www
# mkdir /home/bible/www_log
# mkdir /home/bible/www/weblog
# mkdir /home/bible/ftp
# mkdir /home/bible/ftp_log
# cp index.html /home/bible/www
# chmod 701 /home/bible
# chown -R bible:bible /home/bible
3. DNS에 사용자 도메인 설정하기
# vi /etc/named.conf
zone "bible.co.kr" {
 type master;
 file "bible.co.kr.zone";
};
# vi /var/named/bible.co.kr.one
$TTL 86400
@  IN SOA ns.dns.co.kr. root.bible.co.kr. (
  2006040101 ; serial number
  28800  ; secondaries refresh every 8 hours
  14400  ; if refresh fails, retry every 4 hours
  2419200  ; if cannot refresh, expire IN 30 days
  86400  ; default ttl
)

  IN NS 192.168.0.2
  IN MX 10 bible.co.kr.
  IN A 192.168.0.101
www  IN A 192.168.0.101
ftp  IN A 192.168.0.101
4. 아파치에 가상호스트 설정하기
# vi /usr/local/apache2/conf/httpd.conf
#<VirtualHost *:80>
<VirtualHost 192.168.0.2>
    ServerAdmin webmaster@bible.co.kr
    DocumentRoot /home/bible/www
    ServerName bible.co.kr
    ServerAlias http://www.bible.co.kr/
    ErrorLog /home/bible/www_log/error_log
    CustomLog /home/bible/www_log/access_log common
    Alias /bible/ "/home/bible/www/"
    <Directory /home/bible/www>
        Options ExecCGI
        AllowOverride AuthConfig
    </Directory>
</VirtualHost>
5. 일hit수와 일트랙픽량 제한 설정하기
# vi /usr/local/apache2/conf/httpd.conf
#<VirtualHost *:80>
<VirtualHost 192.168.0.2>
    ServerAdmin webmaster@bible.co.kr
    DocumentRoot /home/bible/www
    ServerName bible.co.kr
    ServerAlias http://www.bible.co.kr/
    ErrorLog /home/bible/www_log/error_log
    CustomLog /home/bible/www_log/access_log common
    Alias /bible/ "/home/bible/www/"
    ThrottlePolicy Volume  2048M 1d
    ThrottlePolicy Request 20000 1d
    <Directory /home/bible/www>
        Options ExecCGI
        AllowOverride AuthConfig
    </Directory>
</VirtualHost>
6. 메일사용을 위한 메일설정하기
# vi /etc/mail/access
bible.co.kr RELAY
# makemap hash /etc/mail/access.db < /etc/mail/access
# vi /etc/mail/local-host-names
bible.co.kr
# vi /etc/mail/virtusertable
webmaster@bible.co.kr bible
# makemap hash /etc/mail/virtusertable.db < /etc/mail/virtusertable
7. 데이터베이스 사용을 위한 MySQL 설정하기
# /usr/local/mysql/bin/mysql -u root -p mysql
Enter password: ********
mysql> create database bible;
mysql> grant all on bible.* to bible@'localhost' identified by '1234';
8. 웹로그분석서비스를 위한 webalizer 설정하기
# cp /etc/webalizer.conf.sample bible.co.kr.conf
# bible.co.kr.conf
LogFile  /home/bible/www_log/access_log
OutputDir /home/bible/www/weblog
HistoryName bible.co.kr
ReportTilte bible.co.kr WebSite
HostName http://www.bible.co.kr/
9. 디스크사용량 제한을 위한 Quota 설정하기
# edquota -u bible
Disk quotas for user bible (uid 600):
Filesytem blocks soft hard inodes soft hard
/dev/sda2 1200 307200 358400 34 0 0
10. 가상 FTP호스팅을 위한 proftpd의 가상호스트 설정하기
# vi /usr/local/proftpd/etc/proftpd.conf
<VirtualHost ftp://ftp.bible.co.kr/>
ServerName  "ftp://ftp.bible.co.kr/ FTP Server"
ServerAdmin  webmaster@bible.co.kr
Port   40001
TranferLog  /home/bible/ftp_log/xferlog
MaxClients  10
MaxClientPerHost 3
11. 아파치, FTP, MySQL, DNS, 메일서비스 재시작하기
# /usr/local/apache2/bin/apachectl restart
# /usr/local/mysql/bin/mysqladmin -u root -p reload
# killall -9 proftpd
# /usr/local/proftpd/sbin/proftpd
# /etc/init.d/sendmail restart
# /etc/init.d/named restart

* 메일포워딩 서비스 설정
.forwar 파일에 포워딩할 이메일주소를 등록한다.

* 도메인포워딩 설정
<meta http-equiv="Refresh" content="0; url=http://soback.kornet.net/~bible">

***********************************************************
* 리눅스 백업서버 구축
***********************************************************

* cron을 이용한 로컬 백업
- 백업 스크립트 작성
# cat backup.sh
#!/bin/sh
tar cvfpz /backup/etc.tar.g /etc
tar cvfpz /backup/usr.tar.g /usr
tar cvfpz /backup/var.tar.g /var
tar cvfpz /backup/home.tar.g /home
# chmod 755 backup.sh
- cron에 등록
# crontab -l
00 04 * * * su - root -c '/root/backup.sh' >& /dev/null

* ncftpget으로 원격자동 백업 구축
A서버 : 백업대상서버
        92.168.0.202
        로컬백업이 구현되어 있음(/backup 에 백업데이터가 매일 백업되고 있음)
B서버 : 백업서버
        192.168.0.211
        /backup 디렉토리에 백업대상서버에서 가져온 데이터를 매일 백업함
A서버의 데이터가 B서버로 매일 자동 백업됨
# cat login.cfg
host 19.168.0.202
user bible
pass 12345678
# cat remotebackup.sh
#!/bin/sh
ncftpget -R -f login.cfg /backup /backup
    -R : 서브디렉토리의 파일들까지 모두 포함하기 위한 옵션
    -f : 로그인설정파일을 지정하기 위한 옵션
    login.cfg : 로그인설정파일
# crontab -l
00 05 * * * su - root -c '/root/remotebackup.sh' >& /dev/null

* DAT테잎으로 백업과 복구하기
- DDS 테잎의 종류
  DDS1 타입 : 비압축으로 2GB까지 저장가능.
  DDS2 타입 : 비압축으로 4GB까지 저장가능. 압축하면 8GB까지 저장가능.
  DDS3 타입 : 비압축으로 12GB까지 저장가능. 압축하면 24GB까지 저장가능.
  DDS4 타입 : 비압축으로 20GB까지 저장가능. 압축하면 40GB까지 저장가능.
- 일반적으로 백업은 DAT라는 테입에 dump형식으로 저장하여 다시 원상복구하기 위하여 restore하는 것을 의미한다.
1. 먼저 imsi디렉토리를 만든다.
   imsi디렉토리는 테잎으로부터 restore되는 파일을 일시적으로 저장하기 위한 것이다.
2. ufsrestore를 실행한다.
   i : interactive모드로 restore를 하겠다느 ㄴ의미, 즉 원하는 파일들만 복구하기 위해 파일을 지정할 수 있는 모드
   v : verbose 지정(자세한 메시지 출력)
   h : hierarchical디렉토리를 생성하며 복구, 즉 백업된 경로대로 디렉토리도 생성하게됨.
# ufsrestore ivh /dev/rmt/0
3. 원하는 파일 찾기
   restore명령어는 다음과 같은 것들이 있다.
   ls      : 현재디렉토리에서 백업테잎에 저장된 파일들 보기
   cd      : 디렉토리 이동
   delete  : 복구할 파일에서 제외
   add     : 복구할 파일 선택
   extract : 복구하기
   pwd     : 현재위치를 절대경로로 표시하기
   quit    : 종료
4. 원하는 파일 선택(add)
5. 파일생성 extract하기
6. 빠져나오기
7. 복구한 파일 확인
8. 파일내용 확인
9. 복구한 파일을 원하는 위치에 복사

* rsync를 이용한 원격네트워크백업서버 구축 실무
- rsync 설치
# wget http://rsync.samba.org/ftp/rsync/rsync-2.6.3.tar.gz
# tar xvfz rsync-2.6.3.tar.gz
# cd rsync-2.6.3
# ./configure
# make
# make install
- 873번 포트를 통한 rsync 네트워크백업 개론
환경:
      백업 대상서버들(백업할 데이터가 있는 서버들)
        SU1: 211.220.1.1
        SU1: 211.220.1.2
        SU1: 211.220.1.3
      백업서버(백업데이터를 가져와 보관할 백업서버)
        BS: 211.220.1.4
1. SU1, SU2, SU3 서버들 자체적으로 cron에 의한 백업을 수행하여 각각의 디렉토리에 저장이 외더 있다.
   이 작업은 각각의 로컬서버에 주기적으로(일별, 주별) 자동백업되어 특정한 디렉토리(backup)에 저장이 된다.
2. BS에서 SU1, SU2, SU3서버에 백업되어 있는 데이터들을 네트워크를 통해 가져오게 된다.
   이 작업은 BS서버의 cron에 의해 수행되며 주기적인(일별, 주별, 월별 등) 작업수행이 가능하며 여기서는 매일 새벽 6시에 데이터를 자뎌오도록 설정한다.
3. 백업대상 서버(SU1, SU2, SU3)의 설정내용
# grep 873 /etc/services
rsync           873/tcp                         # rsync
rsync           873/udp                         # rsync
# grep rsync /etc/hosts.allow
rsync : 211.220.1.4
# cat /etc/xinetd.d/rsync
service rsync
{
 disable   = no
# port   = 873
 socket_type  = stream
# protocol  = tcp
 wait   = no
 user   = root
 server   = /usr/bin/rsync
 server_args  = --daemon
 log_on_failuer  += USERID
}
# /etc/init.d/xinetd restart
# cat /etc/rsyncd.conf
[SU3]    서비스이름. 일반적으로 서버의 이름
path = /host6/backup  백업대상 데이터가 저장된 절대경로
comment = SU3   설명문. 서버이름이나 컨텐츠의 이름 입력
uid = nobody   데이터를 전송할 user명
gid = nobody   데이터를 전송할 group명
user chroot = yes  path에서 설정한 경로를 루트경로로 사용함. 보안을 위해 꼭 yes 로 설정할 것을 권함.
read only = yes   백업대상서버에 wrie할 경우에는 no로 설정. 즉, 백업서버에서 백업대상서버로 데이터를 가져가기만하므로 일반적으로 read only만 설정함.
hosts allow = 211.220.1.4 접근허용할 서버의 IP. 백업서버의 IP입력
max connections = 3  동시에 접속할 수 있는 동시접속자 수. 무제한으로 설정하려면 0을 설정함.
timeout 600   깁ㄴ값은 60초이며 백업서버에서 접근하여 타임아웃될 시간을 설정함.
4. 백업서버(BS)의 서정내용
# grep 873 /etc/services
rsync           873/tcp                         # rsync
rsync           873/udp                         # rsync
# cat /home/hansoo2/backup.sh
#!/bin/sh
rsync -avz 211.220.1.1::SU1/ /home/hansoo2/backup/SU1
rsync -avz 211.220.1.2::SU1/ /home/hansoo2/backup/SU2
rsync -avz 211.220.1.3::SU1/ /home/hansoo2/backup/SU3
  -a : achive mode로서 기존의 속성 및 퍼미션, 소유권 등의 설정내용을 그대로 유지.
  -v : verbose mode로서 작업내용을 상세하게 보여줌.
  -z : 전송속도를 높이기 위해 압축수행 후 전송을 함.
# crontab -l | grep backup
# network backup system
0 6 * * * su - root -c '/home/hansoo2/backup.sh'
- 백업수행시 에러 대책
1. 압축파일상태에러(inflate returned -3에러)
   이 에러는 백업대상이 되는 파일이 gzip으로 압축되어 있을 경우에 rsync의 실행옵션 중 z라는 옵션을 사용했기 때문에 발생하는 에러이디ㅏ.
   압축된 파일을 다시 압축해서 전송하려고 하는 도중에 발생하는 에러로서 자주 발생하는 에러 중 하나다.
   rsync에서 z옵션을 뺀 후에 실행하면 해결할 수 있다.
2. Timeout 관련 에러
   이 에러는 백업대상서버에 있는 /etc/rsyncd.conf 파일내의 timeout값이 너무 작게 설정되어 있어서 발생하는 에러이다.
   /etc/rsyncd.conf파일내의 timeout값을 조금 높게 설정해 준다.
3. Max Connections 관련 에러
   이 에러는 백업대상서버에 있는 /etc/rsyncd.conf파일내의 max connections에 설정된 값 이상의 연결이 시도되었기 때문에 발생한 에러이다.
   백업대상서버의 /etc/rsyncd.conf 파일내의 max connections 항목값을 적당하게 높여주거나 조절해 준다.
4. 전송대상파일 크기에서(out of memory)
   메모리에 관련된 에러로서 해결방법이 꽤 까다로운 에러 중 하나이다.
   전송대상 파일크기가 너무 커서 rsync에서 전송해야할 파일의 체크섬에서 발생하는 에러이다.
   전송대상 파일들의 크기를 점검해서 너무 클 경우에는 파일을 2-3개로 쪼개어서 다시 실행보시면 해결할 수 있다.

* rsync를 이용한 자동 미러링서버 구축
환경 : 원본소스서버의 데이터가 계속 바뀌어도 미러링서버에서 주기적으로 자동 동기화되므로 모두 동일한 데이터를 갖게된다.
       원본소스서버 : 192.168.0.202
                      원본소스가 보관된 서버이며 /var/ftp/pub 디렉토리에 FTP서비스다운로드파일들이 존재하고 있다.
       미러링 서버  : 192.168.0.211
                      주기적으로 원본소스서버의 /var/ftp/pub 디렉토리내의 소스파일들을 가져와서 미러링서버의 /var/ftp/pub 디렉토리에 저장한다.
- 원본소스서버의 설정사항 및 소스데이터 확인
# cat /etc/hosts.allow | grep rsync
rsync : 192.168.0.211
# cat /etc/rsyncd.conf
[SUPERUSER]
path = /var/ftp/pub
comment = SUPERUSER
uid = nobody
gid = nobody
user chroot = yes
read only = yes
hosts allow = 192.168.0.211
max connections = 3
timeout 600
# cat /etc/xinetd.d/rsync
service rsync
{
 disable   = no
 socket_type  = stream
 wait   = no
 user   = root
 server   = /usr/bin/rsync
 server_args  = --daemon
 log_on_failuer  += USERID
}
- 미러링서버의 설정사항
# rsync -avz 192.168.0.202::SUPERUSER /var/ftp/pub
# cat ftp_mirror.sh
#!/bin/sh
rsync -avz 192.168.0.202::SUPERUSER /var/ftp/pub
# crontab -l | grep rsync
00,10,20,30,40,50 * * * * su - root -c '/root/ftp_mirror.s' >& /dev/null

***********************************************************
* 리눅스 커널 컴파일
***********************************************************

* 커널 컴파일 전체 작업 공정표
1. 시스템 데이터 및 중요 파일들 모두 백업하기
2. 커널컴파일 작업위치로 이동하기
# cd /usr/src
3. 컴파일할 리눅스 커널소스 가져오기
# wget ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.10.tar.gz
4. 커널소스 압축 해제하기
# tar xvfz linux-2.6.10.tar.gz
# ln -s linux-2.6.10 linux
# cd linux
5. 커널컴파일을 위한 작업장 청소작업
# make mrproper
6. 커널컴파일 옵션 설정작업
# make menuconfig -> or make config or make xconfig
7. 커널이미지파일 생성을 위한 컴파일 작업
# make bzImage  -> or make zImage or make zdisk or make zlilo
8. 커널모듈생성을 위한 컴파일
# make modules
9. 커널모듈파일 설치하기
# make modules_install
10. 커널이미지파일 및 관련파일복사, grub.conf파일 수정하기
# make install
# ls -l /boot/vmlinu-2.6.10
# ls -l /boot/vmlinuz
# ls -l /boot/initrd-2.6.10.img
# ls -l /boot/System.map-2.6.10
# ls -l /boot/System.map
# ls -l /boot/grub/grub.conf
11. 재부팅
# reboot
12. 새로운 커널버전확인 및 시스템서비스 확인
# uname -r
# uname -a

***********************************************************
* 리눅스서버 보안관리
***********************************************************

* 리눅스서버 기본 보안설정하기(설치직후 초기보안셋팅하기)
1. 중요한 시스템 파일들 퍼미션과 속성 재설정
2. 중요한 시스템파일들 별도 압축보관하기
3. 시스템디스크와 데이터디스크를 가능한 구분하라.
4. 백업데이터 자정공간을 별도의 파티션이나 별도의 디스크를 이용
5. root의 crontab에 로컬백업설정해 두기
6. /etc/xinetd.d/ 에서 불필요한 서비스 파일들 삭제
7. /etc/rc.d/init.d/에서 불필요한 초기화 파일들 삭제
8. /etc/services에서 사용하지 않는 포트정보 주석처리
9. /etc/hosts.allow, /etc/hosts.deny 파일에서 접근허용 설정
10. ssh에서 root의 원격접속 금지설정
# vi /etc/ssh/sshd_config
PermitRootLogin yes -> root 로그인 허용
PermitRootLogin no -> root 로그인 허용하지 않음
11. FTP서비스에서 root의 원격접속 금지설정
# vi proftpd.conf
RootLogin off
12. 일반사용자의 FTP접속에서 상위디렉토리 이동제한 설정하기
# vi proftpd.conf
DefaultRoot ~
DefaultRoot ~ !wheel
13. FTP 익명(anonymous) 접속금지 설정
14. su 명령어 사용제한설정 및 중요한 관리자 명령어 사용제한 설정하기
# chmod 4750 /bin/su
# chown root:wheel /bin/su
# chattr +i /bin/su
# grep wheel /etc/group
wheel:x:10:root,bible,admin
# chmod 750 /usr/sbin/useradd
# chmod 750 /usr/bin/top
# chmod 750 /sbin/fdisk
# chmod 750 /sbin/mkfs
# chmod 750 /sbin/fsck
# chown root:wheel /usr/sbin/useradd
# chown root:wheel /usr/bin/top
# chown root:wheel /sbin/fdisk
# chown root:wheel /sbin/mkfs
# chown root:wheel /sbin/fsck
# chattr +i /usr/sbin/useradd
# chattr +i /usr/bin/top
# chattr +i /sbin/fdisk
# chattr +i /sbin/mkfs
# chattr +i /sbin/fsck
15. root소유의 setuid, setgid 파일 찾아서 조치하기
# find / -user root -perm -4000 -print
16. 기본 실행되는 불필요한 프로세스를 죽이기
17. ntsysv에서 초기 실행할 서비스데몬 내리기
18. 리눅스서버 설치직후 파일시스템의 badblock 점검하기
19. 시스템 정보유출파일 삭제 및 수정하기
    /etc/issue  콘솔에서 로그인 시도시에 보여줄 메시지 저장
    /etc/issue.net 원격에서 로그인 시도시에 보여줄 메시지 저장
    /etc/redhat-release 원격에서 로그인 시도시에 리눅스 배포판 정보를 보여주는 메시지
    /etc/motd  로그인 이후에 보여줄 공지사항 저장
20. rootkit점검을 위한 chkrootkit 설치 및 실행결과 확인
21. 파일 무결성을 위한 tripwire 설치 및 초기화 설정
22. /etc/sysctl.conf파일에서 커널 옵션값 설정하기
23. 로그보안을 위한 원격로그서버 설정
24. /etc/rc.d/rc.local내에 부팅시 실행할 내용 등록
25. 서버의 정확한 시간을 위한 설정
# crontab -l | grep rdate
00 01 * * * su - root /usr/bin/rdate -s time.bora.net && /sbin/clock -w
26. 아파치 설정파일(httpd.conf) 보안설정하기

* setuid, setgid, stickybit 다루기
- 4XXX : setuid 비트를 의미함
- 2XXX : setgid 비트를 의미함
- 1XXX : sticky-bit 비트를 의미함

* 보안을 위한 find
- 지정된 일자 이후에 변경된 모든 파일 찾기
# find / -used 2 -exec ls -l {} \;
  -> 2일전부터 현재까지 변경된 적이 있는 파일들을 검색
- 지정된 파일보다 이후에 생성된 파일 찾기
# find / -newer /root/file1.txt -exec ls -l {} \;
  -> /root/file1.txt라는 파일이 생성된 날짜 이후에 생성된 파일들만을 검색
- root 소유의 setuid파일 찾기
# find / -user root -perm -000 -print
  -> 소유자가 root이고 setuid가 설정되어 있는 파일을 검색
# find / -type f \( -perm -004000 -o -perm -002000 \) -exec ls -lg {}\;
  -> 퍼미션에 setuid가 있거나 setgid가 있는 파일들을 모두 검색
- 서버내의 백도어파일 찾기
# find /dev -type f -exec ls -l {} \;
  -> /dev 내에 존재하는 파이들 중 일반적인 보통파일을 검색
- 서버내부의 .rhosts 파일 찾아서 확인
# find / -name .rhosts -exec ls -l {} \;
- 서버내부에 .bash_history파일을 모두 찾아서 확인
# find / -name .bash_history -exec ls -l {} \;
  -> 시스템전체를 대상으로 .bash_history파일을 찾아서 보여달라는 의미.
- 소유자가 없는 파일 또는 소유그룹이 없는 무적파일을 찾기
# find / -nouser -o -nogroup -print
  -> 소유자가 없거나 소유그룹이 없는 파일을 검색한다.

* 파일속성설정으로 리눅스파일 보안 구현(chattr, lsattr)
chattr 사용법 : # chattr [-RV] [-v 설정버전] [+-=설정모드] 대상파일들
  -R : 서브디렉토리이하까지 그 속성을 변경할 수 있다.
  -V : 자세한 출력모드를 제공한다.
  -v version : 지정된 파일에 버전을 설정할 수 있다.
                [설정모드]
    + : 지정한 속성을 부여한다.
    - : 지정한 속성을 해제한다.
    = : 원래 파일이 가지고 있던 그 속성만을 유지하게 한다.
    -a : 해당 파일을 추가만 할 수 있다.
    -c : 이 속성이 설정된 파일은 커널에 의해 디스크상에 자동적으로 압축된 상태로 저장이 된다.
    -d : 이 속성이 설정된 파일은 dump로 백업이 되지 않는다.
    -i : 이 속성이 지정되어 있다면 해당파일의 변경, 삭제, 이름변경뿐 아니라 파일추가 및 리으파일도 만들수 없게 된다.
    -s : 이 속성이 설정된 파일은 파일삭제가 될 경우에 해당블럭이 모두 0으로 되어버리고 디스크에 다시 쓰기가 발생하나.
    -S : 이 속성이 설정된 파일은 변경이 될 경우에 디스크동기화가 일어나는 효과를 그대로 누릴 수 있다.
    -u : 이 속성을 가진 파일이 삭제가 되었을 경우에는 그 내용이 저장이 되며 삭제되기 전의 데이터로 복구가 가능해진다.
lsattr 사용법 : # lsattr [-RVadv] [대상파일들]
- 파일의 삭제, 변경, 추가등을 불가능하게 하는 속성 설정
# lsattr rc.local
------------- rc.local
# chattr +i rc.local
# lsattr rc.local
----i-------- rc.local
# rm -f rc.local
rm: cannot remove `rc.local': 명령이 허용되지 않음
- 파일의 삭제, 변경, 추가 등을 불가능하게 하는 i속성 제거
# chattr -i rc.local
# lsattr rc.local
------------- rc.local
# rm -f rc.local
#
- 파일의 삭제는 불가능하지만 내용추가는 가능하도록 하는 속성 설정하기
# chattr +a messages
# lsattr messages
-----a------- messages
# rm -f messages
rm: cannot remove `messages': 명령이 허용되지 않음
# chattr -a messages
# lsattr messages
------------- messages
# rm -f messages
#
- 특정 디렉토리내의 모든 파일과 디렉토리에 대하여 한번에 속성부여하고 확인하기
# chattr -R +i directory
- 특정 디렉토리내의 모든 파일과 디렉토리에 부여되어 있는 속성을 동시에 제거하기
# chattr -R -i directory

* nmap
사용형식 : nmap [스캔타입] [옵션] <대상서버[네트워크|IP주소]>
           스캔옵션
    -sS                          : TCP SYN 스캔
    -sT                          : TCP 연결을 스캔
    -sF -sX -sN                  : Stealth FIN, Xmas Tree, 또는 NUll 스캔모드
    -sP                          : Ping 스캐닝
    -sU                          : UDP 스캐닝
    -sO                          : IP 프로토ㅗㄹ 스캔
    -sI <zombi host[:probeport]> : Idle scan
    -sA                          : ACK 스캔
    -sW                          : Window 스캔
    -sR                          : RPC 스캔
    -sL                          : LIST 스캔
- nmap 설치
# wget http://download.insecure.org/nmap/dist/nmap-3.75.tgz
# tar xvf nmap-3.75.tgz
# cd nmap-3.75
# ./configure
# make
# make install
- nmap으로 로컬서버 스캔하기
# nmap -sT -O -v localhost
  -sT : TCP conect에 대한 스캔을 한다.
  -O  : TCP/IP fingerprinting을 통하여 지정한 서버를 확인한다.
  -v : 스캔별과를 자세히 보여준다.
- 원격지의 특정 서버에 대한 스캔작업하기
# nmap -sT -O -v 192.168.0.101
- 원격서버의 UPD 사용포트 스캔작업하기
# nmap -sU -O -v 192.168.0.101

* root의 원격접속제한
# grep PermitRootLogin /etc/ssh/sshd_config
PermitRootLogin no
# grep RootLogin /etc/proftpd/conf/proftpd.conf
RootLogin off

* 포트번호를 바꾸어서 서비스하는 서버보안
# grep telnet /etc/services
telnet          30023/tcp
# /etc/init.d/xinetd restart

* PortSentry를 이용한 실시간 해킹방어 구현
원격지에서 스ㅐㄴ하는 것을 실시간으로 차단할 수 있는 도구이다.
portsentry는 ping이나 기타 다른 도구들을 이용하여 불법적인 스캔을 이지하고 이를 차단하려고 할 때 /etc/hosts.deny파일에 실시간으로 추가되어 서버접근 및 포트스탬을 막는 막강한 보안툴이다.
portsentry는 다음과 같은 프로톸ㄹ의 스캔을 실시간으로 차단할 수 있다.
tcp  프로토콜 스캔차단(basic port-bound TCP mode)
stcp 프로토콜 스캔차단(Stealth TCP scan detection)
atcp 프로토콜 스캔차단(Advanced Stealth TCP scan detection)
udp  프로토콜 스캔차단(basic port-bound UDP mode)
sudp 프로토콜 스캔차단(Stealth UDP scan detection)
audp 프로토콜 스캔차단(Advanced Stealth UDP scan detection)
- 설치
# wget ftp://ftp.superuser.co.kr/secureity/portsentry/portsentry-1.0.tar.gz
# tar xvfz portsentry-1.0.tar.gz
# cd portsentry-1.0
# make linux
# make install
# ls -l /usr/local/psionic/portsentry/
# cat /etc/rc.d/rc.local
#!/bin/sh
/usr/local/psionic/portsentry/portsentry -tcp # tcp  프로토콜 스캔차단
/usr/local/psionic/portsentry/portsentry -stcp # stcp 프로토콜 스캔차단
/usr/local/psionic/portsentry/portsentry -atcp # atcp 프로토콜 스캔차단
/usr/local/psionic/portsentry/portsentry -udp # udp  프로토콜 스캔차단
/usr/local/psionic/portsentry/portsentry -sudp # sudp 포트토콜 스캔차단

* tripwire를 이용한 파일무결성 구현
tripwire는 파일시스템무결성 점검을 하는 서버보안도구로서 파일들의 변경사항유무를 검사할 수 있는 대표적인 보안툴이다.
이 툴을 이용하면 파일들의 추가/삭제/변경유무를 확인할 수 있다.
- 설치
# wget ftp://ftp.superuser.co.kr/security/tripwire/tripwire-2.3-47.bin.tar.gz
# tar xvfz tripwire-2.3-47.bin.tar.gz
# cd tripwire-2.3
# ./install.sh
- 파일확인
/usr/sbin/tripwire  tripwire의 주된 실행파일
/usr/tripwire/*   tripwire에 관련된 설정파일과 key파일 및 정책파일들이 저장될 디렉토리
/var/lib/tripwire/report tripwire실행결과 보고서가 저장될 디렉토리
/var/libtripwire  tripwire DB파일이 저장될 디렉토리
- tripwire 데이터베이스 생성하기(tripwire 초기화)
tripwire 초기화는 점검했던 파일들의 무결성 점검결과를 저장하고있던 DB를 초기화 하다는 것이다.
즉, 초기화시키기 전의 변공사항은 이후에는 적용되지 않는다.
# tripwire --init
- tripwire 실행으로 파일 생성/변조/삭제유무 점거마기
# tripwire --check
- tripwire 설정파일확인 및 변경
twcfg.txt 파일은 tripwire의 주설정파일로서 /etc/tripwire/twcfg.txt 에 존재한다.
이 파일은 tripwire가 어떤 환경으로 실행될 것인가를 정의해둔 파일이다.
즉, tripwire실행파일의 위치, 정책파일(tw.pol)의 위치, tripwire 데이터베이스파일의 위치등을 모두 이 파일에서 정의하고 있다.
- tripwire정책파일 수정 및 변경
tripwire는 /etc/tripwire/twcfg.txt파일을 이용하여 점검할 대상파일을 결정한다.
이 파일을 수정해서 점검할 파일을 추가/삭제할 수 있다.
이 파일에 등록된 파일들은 tw.pol파일(바이너리로 되어있음)에 적용되어 tripwire에서 사용하게 된다.

* chkrootkit을 이용한 rootkit 탐지
백도가 설치되어 있는가를 검사하는 툴이다.
chkrootkit을 이용해서 다음과 같은 검색이 가능하다.
  rootkit의 흔적 및 설치여부를 검색(chkrootkit)
  NIC으 promisc모드 설정여부 검색(ifpromisc)
  lastlog의 변조 및 삭제여부 검색(chklastlog)
  wtmp의 변조 및 삭제여부 검색(chkwtmp)
  기타 파일벼조여부를 검색
chkrootkit툴에는 다음가 같은 파일들이 존재하며 이 파일들은 다음과 같은 역할을 한다.
  ifpromisc.c 랜카드가 promiscuous mode로 설정되어 있는가를 검사
  chklastlog.c /var/log/lastlog의 삭제유무 검사
  chkwtmp.c /var/log/wtmp의 삭제유무 검사
  check_wtmpx.c wtmpx파일의 삭제유무검사(Solaris의 경우만 해당됨)
  chkproc.c LKM트로이목가 검사
  strings.c 점검되는 파일들의 변조여부 검사
- 설치
# chkroot.tar.gz다운로드
# tar xvfz chkrootkit.tar.gz
# cd chkrootkit-0.44
# 컴파일 전 로그파일위치 확인 및 수정하기
  모든 파일을 열어서 로그파일의 정확한 위치를 확인한다.
  chklastlog.c 파일에 리눅스에 대한 설정부분을 추가해 준다.
  #ifdef _LINUX_
  #define WTMP_FILENAME  "/var/log/wtmp"
  #define LASTLOG_FILENAME "/var/log/lastlog"
  #endif
# make sense
- chkrootkit으로 rootkit설치여부 및 서버점검하기
# ./chkrootkit

***********************************************************
* 리눅스 서버관리를 위한 쉘스크립트
***********************************************************

* 쉘프로그램에서 실행상태 반환하는 true와 false
쉘프로그램내에서 실행한 명령의 결과가 정상적으로 종료되었다는 의미로 true를 실행하여 실행이 정상적으로 완료되었음을 알려주는 역할을 한다.
이 때 true가 반한하는 종료상태값은 실행성공을 의미하는 0이다.
그리고 false는 쉘프로그램내에서 특정 명령수행이 항상 실패햤음을 의미하는 1을 반환한다.

* 쉘프로그램에서 사용자 입력값 받아서 처리하기
- 사용자 입력값 처리하는 read문 기본 사용법
read -p "Select Number(1, 2, 3 or 4): " MENU1
화면에 "Select Number(1, 2, 3 or 4): "가 출력이 된 후에 사용자의 입력을 대기한다. 사용자가 입력한 내용을 MENU1에 저장한다.
- 쉘프로그램내에서 read 문에서 배열로 받아서 처리하기
입력내용을 받아들일 지정된 변수를 배열변수로 사용하려면 -a 옵션을 사용해야 한다.
쉘프로그램에서도 C와 마찬가지로 배열인자는 0부터 시작한다.
배열변수를 출력하려면 ${변수명[인자]}와 같은 형식을 사용한다.

* 쉘프로그램 실행시 발생되는 메시지 처리하기
logger는 로그메시지를 시스템로그파일(/var/log/messages 등)에 기록할 수 있는 로그리고기이다.
logger명령어를 사용하여 원하는 로그를 기록하면 지정된 메시지가 시간표시와 함께 시스템로그파일(/var/log/messages)에 기록된다.
사용형식 : logger [-is] [-f file] [-p pri] [-t tag] [메시지]
# logger SystemCheck
# tail -1 /var/log/messages
Apr  3 00:55:03 rh9  4월  3 00:55:03 widemail: SystemCheck

* 반복메뉴방식의 쉘프로그램 만들기
select문은 반복되는 메뉴방식의 쉘프로그램을 만들 때에 최적이다.
사용형식 :
 select 변수 in 메뉴리스트
 do
  실행될 명령문들
 done
select문으로 반복메뉴방식의 쉘프로그램을 작성할 때에는 PS3라는 쉘변수를 사요하는 것이 일반적이다.
즉, PS3쉘변수를 사용하면 PS3에 지정된 메시지를 화면으로 출력하고 사용자의 입력을 기다린다.
select문이 실행되면 in 다음의 메뉴리스트에 번호를 붙여서 화면으로 출력한다.
그리고 선택된 번호가 지정되면 변수에 메뉴리스트에서 선택된 해당리스트를 저장한 다음 "do ~ done"의 내용이 실행된다.

* 동종류 프로세스 한번에 죽이는 쉘명령어 직접만들기
# cat prokill.sh
#!/bin/sh
ps -ef | grep $1 | awk '{ print $2 }' | xargs kill -9 {}

* superuse 사이트에 있는 스크립트를 이용하자.

***********************************************************
* 리눅스서버 응급복구와 삭제파일 복구
***********************************************************

[1] 다운된 리눅스서버 응급복구

* 복구보다는 백업을 우선하라.

* 파일시스템이 깨졌을 때 부팅안되는 상황 조치
# fdisk -l    -> 깨진 파일시스템 확인
# e2fsck -j ext3 /dev/sda3 -> 파일시스템 점검

* 리눅스CD linux rescue 모드로 보구하기
파일시스템이 깨졌을 때 : e2fsck로 파일시스템 복구
비밀번호 및 로그인장애 : /etc/passwd 파일 점검
grub정보로 부팅장애    : /boot/grub/grub.conf 파일 점검
부팅시 마운트정보 장애 : /etc/fstab 점검

* 리눅스CD linux rescue nomount 로 복구하기
현재 상태에서는 장치파일명이 생성되어있지 않으며 마운트도 되어있지 않기 때문에 현재 시스템의 어떠한 수정작업도 할 수 없는 상황이다.
먼저 필요할 것 같은 장치명들을 임의대로 생성한다.
# mknod /dev/sda -> /dev/sda  장치명 생성
# mknod /dev/sda1 -> /dev/sda1 장치명 생성
# mknod /dev/sda2 -> /dev/sda2 장치명 생성
생성한 장치와 마운트할 마운트포인트(디렉토리)를 생성한다.
# mkdir /temp
# mkdir /temp1
# mkdir /temp2
위에서 생성한 각 장치명과 각 디렉토리들을 마운트한다.
# mount /dev/sda1 /temp1
# mount /dev/sda2 /temp2

* 리눅스 시스템업그레이드 방법으로 복구하기

* 시스템 서버 다운시 매직키를 이용한 응급조치
- ALT + SysRq + S : Alt키와 SysRq키와 S키를 동시에 누른다. sync 작업
- ALT + SysRq + E : Alt키와 SysRq키와 E키를 동시에 누른다. tem SIG 작업
- ALT + SysRq + U : Alt키와 SysRq키와 U키를 동시에 누른다. umount 작업
- ALT + SysRq + B : Alt키와 SysRq키와 B키를 동시에 누른다. reboot 작업

[3] 삭제된 파일 복구하기

* 리눅스 휴지통(safedelete)을 이용한 삭제파일 복구하기
safedelete라는 명령어는 파일을 삭제하는 명령어이다.
단 rm과는 달리 safedelete라는 명령어로 파일을 삭제하면 특정한 디렉토리에 삭제된 파일을 보관하게 된다.
그리고 삭제된 사용자의 홈디렉토리에 삭제 파일리스트를 저장하고 있는 로그파일에 그 파일정보들을 보관한다.
그리고 safedelete명령어로 삭제된 파일은 undelete라는 명령어로 복구가 가능하다.
따라서, 이 툴로서 삭제된 파일을 복구하려면 파일삭제시에 사용하는 명령어를 rm 대신에 safedelete 명령어를 사용해야한다.
이 툴의 이름은 safedelete이며 삭제와 복구시에 사용되는 명령어는 다음과 같다.
    safedelete : rm 대신에 사용하게 될 파일보구가 가능한 삭제명령어
    undelete   : safedelete로 삭제한 파일을 복구하는 명령어
    safecnvt   : safedelete 로그파일(.safedelete.log)을 컨버전하는 명령어
    safedelchk : 지정한 일자 이후의 safedelete로 삭제한 파일을 완전 삭제함
- safedelete 설치
# wget ftp://ftp.nuri.net/pub/RedHat/redhat/redhat-7.1-en/powertools/i386/RedHat/RPMS/saedelete-1.3-0.i386.rpm
# rpm -Uvh saedelete-1.3-0.i386.rpm
- rm 명령어를 safedelete로 대체하기
# grep safedelete ~/.bashrc
alias rm='safedelete'

* tct의 unrm으로 삭제파일 복구하기
- 설치
http://www.fish.com/tct 에서 다운로드한다.
# tar xvfpz tct-.09.tar.gz
# cd tct-1.09
# make
# cd bin
# cp rnrm /usr/local/bin
# cp lazarus /usr/local/bin
- 삭제파일 복구
# /usr/local/bin/unrm /dev/sda1 > /tmp_dir/dump_temp
# /usr/local/bin/lazarus -h /tmp_dir/dump_temp

* e2fsck 파일시스템 점검복구유틸리티
- e2fsck 종료코드
  0 - 에러없이 정상적인 종료를 의미함
  1 - 파일시스템을 복구하였음을 의미함
  2 - 파일시스템이 복구되어 시스템이 재부팅되어야함을 의미함.
  4 - 작업대상 파일시스템에 문제가 있으나 복구하지 않고 그대로 두었음을 의미함.
  8 - 실행에러를 의미함
  16 - 사용법 또는 문법에러를 의미함
  32 - e2fsck작업이 사용자에 의해서 취소되었음을 의미함.
  128 - 공유 라이브러리에러를 의미함
- e2fsck작업에서 기본적으로 점검하는 항목
  inode 점검
  blocks 점검
  sizes 점검
  디렉토리구조 점검
  디렉토리 연결성점검
  파일링크 정보
  전체파일갯수 점검
  전체블록수중 사용중인 블록 정검
- 사용형식
  e2fsck [-pacnyrdfvtFV] [-b 수퍼블록] [-B 블록크기] [-l|-L 배드블록목록화일] 장치명
  마운트되어 있는 상태에서 e2fsck실행을 자제한다.
- e2fsck 명령어로 특정 파일시스템의 점검 및 복구하기
# e2fsck /dev/hda1
- e2fsck로 특정 파일시스템 강제 점검 및 복구하기
# e2fsck -f /dev/hda1
- ext3(저널링)파일시스템 점검 및 복구하기
# e2fsck -j ext3 /dev/hda1
- e2fsck로 특정 파일시스템 강제 점검 및 상세 작업내역보기
# e2fsck -fv /dev/hda1
- 파일시스템의 수퍼블록을 이용한 파일시스템 복구
       -b superblock
              Instead  of  using  the  normal  superblock,  use an alternative
              superblock specified by superblock.   This  option  is  normally
              used  when the primary superblock has been corrupted.  The loca-
              tion of the backup superblock is dependent on  the  filesystem's
              blocksize.    For  filesystems  with  1k  blocksizes,  a  backup
              superblock can be found at block 8193; for filesystems  with  2k
              blocksizes,  at  block  16384;  and  for 4k blocksizes, at block
              32768.

              Additional backup superblocks can be  determined  by  using  the
              mke2fs  program  using  the  -n  option  to  print out where the
              superblocks were created.   The -b option to mke2fs, which spec-
              ifies blocksize of the filesystem must be specified in order for
              the superblock locations that are printed out to be accurate.

              If an alternative superblock is specified and the filesystem  is
              not  opened  read-only,  e2fsck  will make sure that the primary
              superblock is  updated  appropriately  upon  completion  of  the
              filesystem check.
# e2fsck -b 8193 /dev/hda1
# e2fsck -b 16385 /dev/sda1
- e2fsck로 파일시스템 점검시 버퍼캐쉬의 내용을 디스크에 저장하기
-F옵션을 사용하면 e2fsck를 수행하기 이전에 sync작업을 수행한다.
# e2fsck -F /dev/sda1
- e2fsck로 특정 파일시스템 점검시 오류 자동수정하기
# e2fsck -p /dev/sda1
- e2fsck로 파일시스템 점검시 모든 질문항목에 yes라고 자동입력하기
# e2fsck -y /dev/sda1
- e2fsck로 파일시스템 점검시 모든 질문항목에 no라고 자동입력하기
# e2fsck -n /dev/sda1





***********************************************************
* 리눅스서버 NFS와 NIS
***********************************************************

* NFS 서버와 NFS 클라이언트의 필요한 데몬들
- rpc.nfsd : NFS 데몬
- rpc.mountd : rpc 기반하에서 NFS 사용을 위한 마운트를 가능하게 하는 데몬
- portmap : NFS가 원래 RPC기반하에서 작동되는 깃이기 때문에 rpcbind를 필요로 하면, rpcbind의 매핑을 위해 필요한 데몬이 portmap이다.
- /etc/exports : 마운트를 허용한 디렉토리와 사용옵션 nfs 설정파일

* NFS 서버와 클라이언트의 데몬 시작, 종료 및 재시작
- NFS 데몬
# /etc/init.d/nfs start
# /etc/init.d/nfs stop
# /etc/init.d/nfs restart
- portmap 데몬
# /etc/init.d/portmap start
# /etc/init.d/portmap stop
# /etc/init.d/portmap restart

* NFS 관련 데몬들의 실행점검
"rpcinfo -p"라고 하면 현재 NFS서버에서 구동중인 NFS관련 데몬들을 점검해 볼 수 있다.
관련된 데몬은  portmapper, mountd, nfs, nlockmgr, rquotad, status 가 있다.
# rpcinfo -p
   프로그램 버전 원형   포트
    100000    2   tcp    111  portmapper
    100000    2   udp    111  portmapper
    100011    1   udp    736  rquotad
    100011    2   udp    736  rquotad
    100011    1   tcp    739  rquotad
    100011    2   tcp    739  rquotad
    100003    2   udp   2049  nfs
    100003    3   udp   2049  nfs
    100021    1   udp  32780  nlockmgr
    100021    3   udp  32780  nlockmgr
    100021    4   udp  32780  nlockmgr
    100005    1   udp  32781  mountd
    100005    1   tcp  33434  mountd
    100005    2   udp  32781  mountd
    100005    2   tcp  33434  mountd
    100005    3   udp  32781  mountd
    100005    3   tcp  33434  mountd
# rpcinfo -p 192.168.0.102
   프로그램 버전 원형   포트
    100000    2   tcp    111  portmapper
    100000    2   udp    111  portmapper
    100011    1   udp    736  rquotad
    100011    2   udp    736  rquotad
    100011    1   tcp    739  rquotad
    100011    2   tcp    739  rquotad
    100003    2   udp   2049  nfs
    100003    3   udp   2049  nfs
    100021    1   udp  32780  nlockmgr
    100021    3   udp  32780  nlockmgr
    100021    4   udp  32780  nlockmgr
    100005    1   udp  32781  mountd
    100005    1   tcp  33434  mountd
    100005    2   udp  32781  mountd
    100005    2   tcp  33434  mountd
    100005    3   udp  32781  mountd
    100005    3   tcp  33434  mountd

* NFS설정파일(/etc/exports)
설정형식 : [마운트할 디렉토리] [허용할 NFS크라리언트](설정옵션들)
# cat /etc/exports
/web_data 192.168.0.101(rw)  # 192.168.0.101 NFS 클라이언트에서 NFS 서버의 /web_data 로 read와 write가 가능하도록 마운트를 허용한다.
/db_data 192.168.0.101(rw)
/data  192.168.0.109(ro)  # NFS서버의 /data 디렉토리를 NFS클라이언트에서 read 만 가능하도록 마운트 허용
/home  192.168.0.109(rw)  # NFS서버의 /home 디렉토리 전체를 NFS 클라이언트가 마운트하도록 허용
/home/sspark 192.168.0.109(noaccess)  # /home/sspark 은 NFS마운트를 허용하지 않는다.
/var/log 192.168.0.109(rw,root_squash) # NFS클라이언트에서 NFS서버로 root권한으로 마운트를 할 경우에 NFS서버에서 root권한을 부여하는 것이 아니라 nfsnobody권한을 부여한다.
        NFS클라이언트에서 NFS서버의 마운트시에 root를 일치시키려면 no_root_squash 옵션을 사용한다.(기본값은 root_squash)
/var/log 192.168.0.109(rw,no_root_squash)# NFS클라이언트그의 root는 NFS서버로 마운트 할 때 NFS서버에서의 root와 일치한다. 가능한 이 설정은 보안을 위해 사요하지 마라.
/home  192.168.0.109(rw,all_squash) # root사용자와는 반대로 일반사용자으 경우에는 no_all_squash가 기본값이다.
        NFS클라이언트에서 NFS서버로 마운트할 때 동일사용자가 존재한다면 root를 제외한 일반사용자는 도일한 사용자로 매핑이 된다.
        all_squash라는 옵션을 사용했기 때문에 일반사용자의 경우에도 모두 nfsnobody로 매핑이 된다.

* NFS마운트 방법
설정형식 : mount -t nfs NFS서버_IP주소_또는_호스트명:/NFS서버_마운트포인트 /NFS클라이언트_마운트포인트
           -t nfs   파일시스템형식을 nfs로 지정
    NFS서버_IP주소_또는_호스트명 NFS서버의 IP주소 또는 호스트명
    NFS서버_마운트포인트  NFS서버에서 NFS클라이언트의 마운트가 허용되어 있는 위치를 지정
    NFS클라이언트_마운트포인트 NFS클라이언트의 마운트포인트 지정
# mount -t nfs 192.168.0.102:/web_data /web_data
# mount -t nfs 192.168.0.103:/db_data /db_data

* 부팅시 NFS 자동마운트 설정
/etc/fstab 에서 사용가능한 NFS 마운트 옵션들
- rsize=n 지정된 NFS서버로부터 읽어오는 바이트수를 지정. 기본값은 1024
- wsize=n 지정된 NFS서버에 쓰기를 할 때 사용하는 바이트수를 지정. 기본값은 1024
- timeo=n RPC타임아웃이 발생되고 나서 첫번째 재전송요구를 보낼 때 사용되는 시간
- retrans=n timeout이 발생시 재전송시도 횟수를 제한한 것임. 기본값은 3
- port=n 지정된 NFS서버와 연결할 때의 포트번호 지정
- fg  첫번째 마운트시도하여 timeout되면 바로 중단됨. default값
- intr  timeout발생시 신호를 보내는 NFS호추를 인터럽트함.
- hard  timeout이 발생하면 "server not responding"이라는 메시지를 출력한 후에 계속 재시도 함
- soft  timeout이 발생하면 I/O에러가 발생하였을을 알려줌.
# cat /etc/fstab | grep nfs
192.168.0.100:/home/bible /bible nfs timeo=10,intr


***********************************************************
* 리눅스 서버이전
***********************************************************

* 서버이전시 고려할 사항들
- 서버 네트워크환경문제
- 일반사용잘의 ID와 패스워드문제
- 사용자들의 도메인문제
- 서버내의 홈페이지 데이터들의 문제
- 아파치웹서버 이전문제
- MySQL 데이터베이스 이전문제

* 서버이전 계획표 작성
구분   이전하기전 서버(A)  이전후 서버(B)
작업자   김길동
작업일시  2006년 4월 5일 새벽 04시부터 06시까지
지역   용산구    구로구
IDC(업체)명  A-IDC    B-IDC
IDC(업체)담당자명 박길동    이길동
IDC(업체)연락번호 111-1111   222-2222
서버모델명(서버명) SU-S2001(A서버)   SU-2002(B서버)
서버IP주소  192.168.0.101   192.168.1.211
네트워크주소  192.168.0.0   192.168.1.0
브로드캐스트주소 192.168.0.255   192.168.1.255
넷마스크주소  255.255.255.0   255.255.255.0
웹서버(위치)  APACHE(/usr/local/apace2) APACHE(/usr/local/apace2)
DB서버(위치)  MySQL(/usr/local/mysql)  MySQL(/usr/local/mysql)
FTP서버   Proftpd(/usr/local/proftpd) Proftpd(/usr/local/proftpd)
DNS서버   BIND9    BIND9
메일서버  SENDMAIL, Qpopper  SENDMAIL, Qpopper
사용자수  10명    10명
기타   gd,libpng,freetype,zlib 등 gd,libpng,freetype,zlib 등
주의사항  이전하기전 사용 도메인 TTL값 최소화
   cron설정주의
   로그파일들 이전주의(/var/log)
   이전후 홈페이지로딩테스트 및 게시판업로드테스트
   작업 7일전 작업관련 홈페이지 공지사항 등록
   각 사용자에게 작업관련 메일발송

* 서버에서 서비스하던 도메인들의 TTL값 변경하기
서버이전 작업을 시작하기 쵯 몇일전에 각 도메인들의 DNS정보가 세팅된 네임서버에서 각 도메인들의 zone파일들에 설정된 TTL값을 0또는 1로 설정한다.
즉, 도메인의 IP주소를 변경하기 전에 DNS의 TTL값을 0 또는 1로 설정해 둔다.

* 서버의 서비스데몬들 중지하기
- 웹서비스 중지
- 메일서비스 중지
- 데이터베이스 서버 중지
서비스를 중지하는 이유는 서버이전을 하는 도중에 계속 서비스를 하게 되면 작업하는 동안에 업데이트된 데이터들은 유실되기 때문이다.

* 이전대상서버에서 네트워크 설정작업
다음과 같이 네트워크 설정명령어들과 파일들을 통하여 새로운 네트워크 환경을 설정한다.
- route      서버라우팅 설정 명령어
- ifconfig     네트워크 인터페이스 설정 명령어
- netconfig     네트워크 설정 명령어
- /etc/resolv.conf    DNS설정 파일
- /etc/sysconfig/network   기본게이트웨이 설정파일
- /etc/sysconfig/network-scripts/ifcfg-eth0 첫번째 NIC 네트워크 설정파일
- /etc/sysconfig/network-scripts/ifcfg-eth1 두번째 NIC 네트워크 설정파일
- /etc/init.d/network    설정된 네트워크 start|stop|restart

* 사용자 계정정보 이전하기
- /etc/passwd파일내의 사용자계정부분 이전하기
  A서버의 /etc/passwd 파일에서 일반사용자들의 계정정보만을 복사해서 B서버의 /etc/passwd에 복사한다.
- /etc/group파일내의 사용자 그룹설정 부분 이전하기
  A서버의 /etc/group파일에서 시스템사용 그룹설정 부분을 제외한 나머지 그룹부분들을 그대로 복사하여 B서버의 /etc/group파일에 그대로 추가한다.
- /etc/shadow 파일내의 사용자 설정부분 이전하기
  /etc/shadow파일에서 사용자들의 정보만을 복사해서 B서버의 /etc/shadow에 복사한다.

* 일반사용자 홈디렉토리 파일들 이전하기
- A서버에서 home을 모두 압축한다.
# cd /
# tar cvfpz home.tar.gz home
- B서버에서 home.tar.gz 파일을 가져온다.
# cd /
# ncftp -u bible 192.168.0.101
ncftp / > get /home.tar.gz
- B서버에서 가져온 home.tar.gz 압축을 해제한다.
# tar xvfpz home.tar.gz -C /

* APACHE 웹서버 이전하기
아파치를 이전할 때에는 다음과 같은 주의사항이 있다.
1. httpd.conf파일 내에 설정되어 있는 가상호스트정보 수정
2. httpd.conf파일 내의 DocumentRoot정보의 확인 및 수정
3. httpd.conf파일 내의 ServerName정보의 확인 및 수정
4. httpd.conf파일 내의 UserDir정보의 확인 및 수정
- A서버에서 APACHE 디렉토리를 압축한다.
# cd /usr/local
# tar cvfp apache2.tar.gz apache2
- B서버에서 A서버에 있는 apache2.tar.gz 파일을 가져오고 압축을 해제한다.
# cd /usr/local
# ncftp -u bible 192.168.0.101
ncftp /usr/local > get /usr/local/apache2.tar.gz
# tar xvfpz apche2.tar.gz
- httpd.conf 파일 수정
  NameVirtualHost 값 수정
  VirutalHost 부분 수정
  DocumentRoot 저옵 수정
  ServerName 정보 수정
- php와 Zend 이전

* MySQL 데이터베이스 이전하기
- A서버에서 MySQL을 압축한다.
# cd /usr/local
# tar xvfpz mysql.tar.gz mysql
- A서버에서 압축한 파일을 B서버로 가져오고 압축을 해제한다.
# cd /usr/local
# ncftp -u bible 192.168.0.101
ncftp /usr/local > get /usr/local/mysql.tar.gz
# tar xvfpz mysql.tar.gz
- MySQL 실행에러문제 해결하기
  호스트명 인식불능문제
  /usr/local/mysql/var/호스트명.pid
  /usr/local/mysql/var/호스트명.err
  /usr/local/mysql/var 소유자 계정확인

* 메일서버 이전하기
- A서버에서 /etc/mail 디렉토리를 압축하여 B서버로 가져온 후에 원래위치에 압축해제한다.
- A서버의 /usr/local/lib/popper파일을 B서버로 가져와서 원래위치에 넣어둔다.
- A서버의 /var/spool/mail 디렉토리내용과 /var/spool/mqueue 디렉토리의 내요을 그대로 가져와서 B서버에 넣어둔다.
- A서버의 /etc/xinetd.d 디렉토리내에 있는 pop3파일을 B서버로 가져와서 넣어둔다.
- A서버의 /usr/sbin/sendmail 파일과 /etc/aliases 파일을 B서버로 가져와서 각자 위치에 넣어둔다.
- 이와 같이 작업한 후에 B서버에서 /etc/init.d/sendmail 스크립트를 이용하여 sendmail을 재시작하고 /etc/init.d/xinetd 스크립트를 재시작하여 /etc/xinetd.d/pop3 파일이 적용되도록 한다.

* 기타 서버이전시 작업해야할 사항들
- A서버의 /etc/hosts파일의 정보를 확인하고 필요한 정보는 B서버의 /etc/hosts파일에 등록한다.
- A서버의 /etc/hosts.allow파일과 /etc/hosts.deny파일의 내용을 B서버에 그대로 설정한다.
- A서버의 /var/spool/cron 디렉토리내의 파일들을 B서버로 그대로 가져와서 설정한다.
- A서버의 /var/log 디렉토리내에 존재하는 필요한 로그파일들(특히 wtmp, secure 파일)을 B서버로 가져와서 /var/log에 저장한다.
- A서버의 /etc/sysctl.conf 파일의 내용을 B서버의 서버사양에 맞도록 수정하여 적용한다.
- A서버의 /etc/motd, /etc/issue, /etc/issue.net 파일들의 니용을 B서버로 가져와서 해당 파일들에 저장한다.
- A서버의 /etc/rc.d/rc.local 파일을 그대로 가져와서 B서버에 복사한다.

* DNS 서버에서 해당 도메인들의 IP주소 변경작업
도메인들의 DNS서버에서 각 도메인들의 IP주소를 B서버의 IP주소(192.168.1.211)ㄹ 변경한다.
TTL값을 원래의 값으로 복원한다.

* 서버이전후 확인할 사항들
1. 홈페이지가 정상로딩 테스트
2. 홈페이지의 게시판 및 업로드 테스트하기
3. 사용자계정 정보확인 및 로그인 테스트
4. 메일수발신 테스트와 MySQL접속 테스트
5. B서버에서 응용프로그램들의 경로 확인하기

***********************************************************
* PROC 파일시스템을 이용한 커널최적화 및 시스템튜팅
***********************************************************

* /proc파일시스템에 존재하는 커널파라미터 변경하는 방법들
1. /etc/sysctl.conf 파일에 등록한다.
   지속적으로 적용하려면 이 파일에 넣어둔다.
2. sysctl 명령어를 이용한다.
   "sysctl -w 파라미터변수=값"의 형식으로 변경이 가능하다.
3. /proc/sys 디렉토리내에 존재하는 각가의 파일들을 vi로 열어서 직접 변경한다.
   좋지않은 방법으로 추천하지 않는다.
4. echo 명령어를 이용한다.
   "echo 1 > 파일위치"와 같은 방법으로 간편하게 수정할 수 있다.
5. redhat-config-proc을 이용한다.

* sysctl을 이용한 커널 옵션 설정 및 확인
- sysclt
  configure kernel parameters at runtime 으로서 커널 파라미터를 설정하는 명령어이다.
- 커널옵션값 전체 확인하기
# sysctl -a
- 특정커널옵션값의 설정
# sysctl -w net.ipv4.tcp_syncookies=1

* /proc/sys/kernel에서 획득할 수 있는 몇가지 커널 정보들
- 서버의 호스트네임 확인
# cat /proc/sys/kernel/hostname
- 커널버전 확인
# cat /proc/sys/kernel/osrelease
- 운영체제명 확인
# cat /proc/sys/kernel/ostype
- 커널 매직키(Magic Key)사용가능 확인
# cat /proc/sys/kernel/sysrq

* /proc/sys/net/ipv4에서 설정 가능한 서버보안
- 외부의 ping 막기
# cat /proc/sys/net/ipv4/icmp_echo_ignore_all
0
# echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
- smurf 공격방지를 위한 broadcast 패킷방지하기
# cat /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts
0
# echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts
- 세션 종료시간 조정
  tcp_fin_timeout은 TCP 세션이 종료된 후에 얼마나 오랫동안 세션연결을 유지하고 있을 것인가를 설정한다.
# cat /proc/sys/net/ipv4/tcp_fin_timeout
60
# echo 20 > /proc/sys/net/ipv4/tcp_fin_timeout
- tcp keepalive time 설정
# cat /proc/sys/net/ipv4/tcp_keepalive_time
7200
# echo 1200 > /proc/sys/net/ipv4/tcp_keepalive_time
- 서버에서 로컬 포트사용범위 설정
# cat /proc/sys/net/ipv4/ip_local_port_range
32768 61000
- IP 포워딩(forwarding)기능 막기
# cat /proc/sys/net/ipv4/ip_forward
0
- 서버의 날짜정보 유출막기
# cat /proc/sys/net/ipv4/tcp_timestamps
1
# echo 0 > /proc/sys/net/ipv4/tcp_timestamps
- SYN_Flooding 공격 막기
  일종의 서비스거부공격(DoS: Denial of Service)의 일종이다.
  TCP세션을 맺기 위해서는 패킷을 보낼 서버와 받을 서버간에 몇단계 확인작업을 거치게 되는데 SYN과 ACK패킷을 이용하여 송수신 준비확인을 한다.
  SYN패킷을 박은 목적지 서버는 SYN과 ACK패킷을 보낸 후에 소스서버에서 ACK패킷을 보내기를 기다리게 된다.
  이 때에 계속적으로 기다리는 것이 아니라 백로그큐(Backlog Queue)가 허용하는 공간에 연결정보를 보관한다.
  이런 상태가 숟없이 쌓이게 되면 복적지 서버의 특정 서비스가 정상적으로 이루어 지지않는 서비스다운상태가 된다.
  해결방법은 백로그큐를 늘려주거나 tcp_syncookies값을 로 설정하는 것이다.
# cat /proc/sys/net/ipv4/tcp_syncookies
0
# echo 1 > /proc/sys/net/ipv4/tcp_syncookies
# cat /proc/sys/net/ipv4/tcp_max_syn_backlog
1024

* /proc/sys/net/ipv4/conf 에서 가능한 네트워크 설정
- ICMP redirect의 변조된 패킷 차단(accept패킷 차단설정)
# cat /proc/sys/net/ipv4/conf/default/accept_redirects
1
# echo 0 > /proc/sys/net/ipv4/conf/default/accept_redirects
- ICMP redirect 의 변조된 패킷 차단(send 패킷 차단설정)
# cat /proc/sys/net/ipv4/conf/default/send_redirects
1
# echo 0 > /proc/sys/net/ipv4/conf/default/send_redirects
- DoS공격 source로 사용 차단(IP 스푸핑 방지하기)
# cat /proc/sys/net/ipv4/conf/default/rp_filter
1
# echo 1 > /proc/sys/net/ipv4/conf/default/rp_filter
- 스푸핑등의 패킷에 대한 로그를 남기자
# cat /proc/sys/net/ipv4/conf/default/log_martians
0
# echo 1 > /proc/sys/net/ipv4/conf/default/log_martians
- source route 패킷허용 막기(신뢰받는 호스트로 위장하는 것 막기)
# cat /proc/sys/net/ipv4/conf/default/accept_source_route
0
# echo 0 > /proc/sys/net/ipv4/conf/default/accept_source_route

* /proc/sys/fs 에서의 옵션설정
- 커널에서 사용할 수 있는 푀대파일 수 설정하기
# cat /proc/sys/fs/file-max
102709
- 현재 활동중인 파일 수 확인
# cat /proc/sys/fs/file-nr
834     0       102709

* /proc에서 커널정보 확인하기
- cpu 정보 확인
# cat /proc/cpuinfo
- 사용중이거나 사용가능한 장치(device)파일 확인
# cat /proc/devices
- 현재 커널에서 인식되어 있는 파일시스템 타입 확인
# cat /proc/filesystems
- 현재 사용되고 있는 메모리 번지별 사용내역 확인
# cat /proc/iomem
- 입출력포트의 번지별 사용내역
# cat /proc/ioports
- 서버 펴윤부하율 정보 확인
# cat /proc/loadavg
- 메모리 일반정보 확인
# cat /proc/meminfo
- 커널에 로드ㅗ디어 있는 모듈확인
# cat /proc/modules
- 현재 마운트되어 있는 정보확인
# cat /proc/mouts
- 현재 설정된 파티션정보 확인
# cat /proc/partitions
- 커널버전 정보확인
# cat /proc/version
- IRQ값 정보확인
# cat /proc/interrupts

***********************************************************
* SAMBA 서버
***********************************************************

* 설치
삼바서버패키지 : samba-3.0.8-0.pre1.3
삼바클라이언트 패키지 : samba-client-3.0.8-0.pre1.3
삼바서버와 클라이언트에서 모두 사용하는 파일들 패키지 : samba-common-3.0.8-0.pre1.3
삼바서버 웹관리 패키지 : samb-swat-3.0.8-0.pre1.3
삼바서버 설정툴 패키지 : system-config-samba-1..21-1
# wget http://ftp.superuser.co.kr/pub/fedora/core3/RPMS/samba-3.0.8-0.pre1.3.rpm
# wget http://ftp.superuser.co.kr/pub/fedora/core3/RPMS/samba-client-3.0.8-0.pre1.3.rpm
# wget http://ftp.superuser.co.kr/pub/fedora/core3/RPMS/samba-common-3.0.8-0.pre1.3.rpm
# wget http://ftp.superuser.co.kr/pub/fedora/core3/RPMS/samb-swat-3.0.8-0.pre1.3.rpm
# wget http://ftp.superuser.co.kr/pub/fedora/core3/RPMS/system-config-samba-1..21-1.rpm
# rpm -Uvh --nodeps samba-3.0.8-0.pre1.3.rpm
# rpm -Uvh --nodeps samba-client-3.0.8-0.pre1.3.rpm
# rpm -Uvh --nodeps samba-common-3.0.8-0.pre1.3.rpm
# rpm -Uvh --nodeps samb-swat-3.0.8-0.pre1.3.rpm
# rpm -Uvh --nodeps system-config-samba-1..21-1.rpm
설치된 주요 파일들
/etc/logrotate.d/samba : 삼바로그를 관리하기 위한 삼바로그 로테이트 스크립트 파일
/etc/init.d/smb : 삼바데몬(nmbd, smbd)을 실행(종료, 재시작)하기 위한 스크립트파일
/etc/sysconfig/samba :삼바 제어판 설정파일
/usr/bin/smbstatus : 삼바서버에 로그인한 정보를 확인하기 위한 유틸리티
/usr/sbin/nmbd : 삼바 NetBIOS 데몬
/usr/sbin/smbd : 윈도우서버와 파일 및 프린터공유를 위한 삼바 주데몬
/usr/bin/smbclient : 삼바 클라이언트 유틸리티로서 삼바서버로 접속이 가능하도록 제공함.
/usr/bin/smbmount : 삼바 원격마운트 유틸리티
/usr/bin/smbumount : 삼바 원격마운트 해제 유틸리티
/etc/samba : 삼바의 주된 디렉토리
/etc/samba/lmhosts : 삼바서버 NetBIOS 호스트파일(삼바서버에서 사용하는 호스트정보파일)
/etc/samba/smb.conf : 삼바서버의 주설정파일
/usr/bin/smbpasswd : 삼바사용자 생성, 삭제 및 삼바사용자 패스워드 설정, 변경 유틸리티
/usr/bin/testparm : 삼바서버 설정파일(smb.conf)점검 유틸리티
/etc/xinetd.d/swat : 삼바서버의 웹관리를 위한 xinetd환경의 삼바설정파일
/usr/sbin/swat : 삼바서버 웹관리툴
/usr/bin/system-config-samba : X환경에서의 삼바설정 유틸리티
/var/log/samba/ : 삼바로그 저장 디렉토리
/etc/samba/smbusers : 리눅스ID와 삼바ID가 다를 경우에 이를 매칭하기 위한 매칭 테이블파일

* 삼바서비스 데몬
- 삼바데몬 시작
# /etc/init.d/smb start
# /usr/sbin/smbd -D
# /usr/sbin/nmbd -D

* 삼바 설정파일 smb.conf 다루기
/etc/samba/smb.conf 파일의 설정에는 다음과 같이 기본적으로 세부분으로 나뉘어져 있습니다.
  1. 글로벌 설정: 이 설정은 삼바서버가 공유하는 자원들에 공통적으로 적용할 기본값을 설정하는 곳이다.
                  이 글로벌설정은 [global]이라는 선언으로 시작하는 부분이다.
  2. 자동홈디렉토리 : 리눅스 사용자들의 홈디렉토리를 로그인홈으로 사용하기 위한 설정으로서 [homes]로 시작하는 부분의 설정이다.
  3. 프린터설정 : 네트워크 공유프린트에 대한 설정부분으로서 [printers]로 시작하는 부분의 설정이다.
- unix charset = cp949
  dos charset = cp949
  display charset = cp949
  리눅스(유닉스) 문자셋, 도스문자셋, 그리고 디스플레이 문자셋을 각각 설정한다.
- workgroup = superuser
  workgroup옵션에는 NT도메인명 또는 워크그룹명을 지정하면 된다.
  작업그룹명이므로 윈도우에서 Workgroup으로 사용하는 이름을 입력한다.
- server string = SUPERUSER File Server
  삼바서버의 이름쯤으오 생각하면 된다.
- hosts allow = 192.168.1. 192.168.2. 127.
  이 옵션은 삼바서버의 보안을 위하여 매우 중요한 옵션이다.
  이 옵션은 삼바서버로의 접근을 허용하기 위한 설정이다.
- load printers = yes
  자동프린트 목록을 사용하기 위한 값으로 yes를 주면 된다.
  네트워크 프린터를 삼바서버에서 관리하도록 하려면 반드시 yes로 설정한다.
- printcap name = /etc/printcap
  printcap파일의 위치가 다른 곳에 있다면 그곳을 지정한다.
- printing = bsd
  사용하는 프린터가 표준이 아니라면 주석처리를 하는 것이 좋다.
  현재 지원되는 프린터시스템의 종류로는 bsd, sysv, plp, lprng, aix, hpux, qnx 등이 있다.
- guest account =pcguest
  삼바서버의 guest사용자를 허용하고자 할 때에는 이 주석을 제거한다.
  이 옵션은 삼바서버에 손님권한으로 접속할 때 어떤 권한을 부여할 것인가를 설정한다.
  가능한 시스템에 특별한 권한이 없는 nobody와 같은 권한으로 설정한다.
- log file = /var/log/samba/log.%m
  이 설정은 삼바서버로 접속하는 개별 사용자들의 호스트정보를 %m 으로 받아서 개별 로그파일을 생성하도록 한다.
- log file = /var/log/samba/smbd.log
  접속한 호스트별로 로그파일을 사용하지 않는다면 하나의 로그파일을 사용할 수도 있다.
- max log size = 50
  로그파일의 용량크기를 KB단위로 제한하기 위한 옵션이다.
  제한하지 않으려면 0을 입력한다.
- security = share
  보안모드를 설정하는 것으로서 대부분의 삼바접속자들에게는 user레벨이 가장 알맞다.
  user레벨을 설정하면 삼바서버에 접속하는 사용자는 반드시 윈도우에서 사용하는 로그인ID와 동일해야 한다.
  만약 위의 설정과 같이 share레벨은 공유디렉토리등에 설정하는 것으로서 ID와 패스워드의 인증엇이 접속하는 것을 허용하는 레벨이다.
  또한 server레벨은 별도의 인증서버에서 ID와 패스워드인증을 받도록하는 레벨이다.
  가능하면 삼바서버보안을 위하여 user레벨을 사용하는 것이 좋다.
- password server = <NT-Server-Name>
  위의 security 옵션값이 server로 설정되었을 때에만 설정할 수 있는 옵션으로서 인증서버로 사용할 서버를 지정하는 옵션이다.
- password level = 8
  패스워드 문자로 대소문자를 조합하여 사용할 문자의 개수를 지정한 옵션이다.
- username level = 8
  삼바사용자명을 대소문자 조합하여 사용할 문자의 개수를 지정한 옵션이다.
- encrypt passwords = yes
  패스워드를 암호화하여 사용하려면 "encrypt passwords" 옵션값을 yes로 설정한다.
- smb passwd file = /etc/samba/smbpasswd
  이 옵션은 삼바사용자들의 암호파일의 위치를 지정한 것이다.
- unix password sync = Yes
  passwd program = /usr/bin/passwd %u
  passwd chat = *New*UNIX*password* %n\n *ReType*new*UNIX*password* %n\n *passwd:*all*authenticateion*tokens*updated*successfully*
  삼바사용자가 원격지에서 삼바패스워드를 변경할 수 있도록 하기위한 설정이다.
  이 설정을 사용하여 원격에서 패스워드를 변경하도록 허용하려면 앞의 encrypt passwords옵션값에 yes라고 해야하고 smb passwd file옵션에 반드시 삼바사용자패스워드파일의 경로를 지정한다.
- username map = /etc/samba/smbusers
  대부분 삼바에서 사용하는 ID와 리눅스계정ID는 동일하게 사용한다.
  만약 삼바사용자명과 리눅스 계정사용자명을 다르게 사용할 경우에 이를 매칭할 수 있도록 하기 위한 옵션이다.
  매칭테이블 파일을 /etc/samba/smbusers 파일로 사용하는 설정이다.
- include = /etc/samba/smb.conf.%m
  이 옵션은 삼바접속자의 플랫폼에 따라서 각기 다른 설정파일을 적용할 수 있도록 지원하기 위해 사용하는 옵션이다.
  %m 은 접속자 시스템의 NetBIOS이름으로 대체되어 접속한 사용자의 플랫폼종류에 따라서 각기 다른 삼바설정파일을 적용할 수 있다.
- socket options = TCP_NODELAY SO_RCVBUF=8192 SO_SNDBUF=8192
  삼바서비스 성능향상을 위한 옵션이다. speed.txt 파일을 참조하라.
- interfaces = 192.168.12.2/24 192.168.13.2/24
  삼바서버에서 두개 이상의 네트워크 인터페이스(NIC)를 사용하도록 지원하기 위한 옵션이다.
- remote browse sync = 192.168.3.25 92.168.5.255
  remote annouce = 192.168.1.255 192.168.2.44
  지역서브넷에서 삼바서버를 잘 인식하도록 하기 위하여 자기자신을 알리도록 한다.
- local master = no
  이 옵션은 특정 서브네트워크내에서 삼바서버가 로컬마스트브라우즈가 되도록 하는 옵션이다.
- os level = 33
  이 옵션은 마스터브라우즈 선거(master browser elections)에서 이 서버의 우선구너을 가질 수 있도록 허용한다.
- domain master = yes
  삼바서버가 Domain Master Browser가 되도록 한다.
  삼바가 Domain Master Browser가 되면 서브네트워크들로부터 브라우즈 목록을 수집하는 역할을 한다.
- domain logons = yes
  삼바서버를 윈도우95기반의 워크스테이션에 대한 도메인로그인서버 역할을 하도록 하려면 이 옵션을 사용하라.
- logon script = %m.bat
  각 플랫폼별 또는 각 사용자별로 로그온스크립트(login script)를 구분하여 사용할 수 있도록 한다.
- logon path = file://%25L/Profiles/%25U
  오직 윈도우95 또는 윈도우NT에서 로우빙프로파일(roving profile)을 어디에 저장해 둘것인가를 지정하는 설정이다.
- wins support = yes
  윈도우 인터넷네임서비스인 WINS를 지우너하기 위한 섹션이다.
- wins server = w.x.y.z
  WINS 서버를 지정한다.
- wins proxy = yes
  WINS프락시 : WINS기능이 없는 클라이언트 대신 질의에 답하도록 하기위한 옵션이다.
- dns proxy = no
  DNS의 nslookup을 사용하여 NetBIOS이름을 찾을 것인가 아닌가를 지정하는 옵션이다.
- preserve case = no
  short preserve case = no
  대소문자를 유지보존할 것인가를 지정하는 옵션이다.
- default case = lower
  DOS파일들의 기본 문자는 대문자로 인식한다.
  만약 lower로 설정하면 소문자로 인식한다.
- case sensitive = no
  대소문자의 구분을 할것인가 말것인가를 지정하는 옵션이다.
- [homes]
  comment = Home Directories
  browseable = no
  writable = yes
  사용자들의 홈디렉토리서버스에서 사용하는 기본적인 설정값이다.
- [netlogon]
  comment = Network Logon Service
  path = /home/netlogon
  guest ok = yes
  writable = no
  share modes = no
  도메인로그온 기능을 사용하기 위한 netlogin을 위한 설정이다.
- [Profiles]
  path = /home/profiles
  browseable = no
  guest ok = yes
  로우빙프로파일(roving profile)을 지정하려면 주석을 제거한다.
- [printers]
  comment = All Printers
  path = /var/spool/samba
  browseable = no
  guest ok = no
  writable = no
  printable = yes
  BSD타이의 프린트시스템을 가지고 있다면 개별 설정없이 바로 사용할 수 있다.
- [tmp]
  comment = Temporary file space
  path = /tmp
  read only = no
  public = yes
  삼바사용자들의 임시공유 디렉토리로 사용하기 위한 설정이다.
- [public]
  comment = Public Stuff
  path = /home/samba/public
  public = yes
  read only = no
  write list = @staff
  삼바사용자라면 누구나 접근이 가능한 공유디렉토로서 staff그룹에 등록된 사용자들을 제외한 일반사용잗ㄹ은 읽기전용으로만 사용할 수 있다.

* 삼바클라이언트 유틸리티 smbclient
사용형식 : smbclient '\\서버\서비스' [-U 삼바사용자] [패스워드] [옵션]
# smbclient -L localhost  -> 리눅스서버에 설치되어 있는 삼바서버의 접속 및 운용상태 확인
# smbclient '\\files\papa' -U papa -> 원격지의 file이라는 로스트로 papa라는 계정을 이용하여 삼바서버로 접속
  서버명으로 file이라는 호스트명을 사용하려면 /etc/samba/lmhosts파일에 호스트정보가 등록되어 있어야 한다.
# smbclient -L 192.168.0.100
# smbclient -U papa -L 192.168.0.100

* 삼바 원격마운트를 위한 smbmount와 smbumount
사용형식 : smbmount //호스트명(또는 IP)/서비스 /마운트포인트
           smbumount /마운트포인트
# smbmount //192.168.0.100 /tmp/share
# smbumount /tmp/share

* 삼바 사용자 관리
- 새로운 삼바사용자 생성과 패스워드 설정하기
  삼바서버에서 사용자를 드록하기 위해서는 smbpasswd라는 명령어를 사용한다.
  삼바사용자의 삼바패스워드 또는 smbpasswd라는 명령어를 사용한다.
  삼바사용자를 새로 생성하기 전에 리눅스 계정생성을 먼저해야 한다.
# useradd dhan
# passwd dhan
# smbpasswd -a dhan
- 삼바사용자 패스워드 변경하기
  root 사용자일 경우 : smbpasswd [options] [username]
  root 사용자가 아닌 경우 : smbpasswd [options]
- 삼바사용자 사용하지 못하도록 사용중지 설정하기
# smbpasswd -d dhan -> 사용 중지
# smbpasswd -e dhan -> 사용 재개
- 삼바사용자 삭제하기
# smbpasswd -x dhan

* 삼바서버의 로그인정보 확인을 위한 smbstatus
사용형식 : smbstatus [-P] [-b] [-d] [-L] [-p] [-S] [-s <설정파일>] [-u 삼바계정]
# smbstatus  -> 현재 삼바서버로 로그인되어 있는 사용자들을 확인
# smbstatus -b  -> 삼바서버로 로그인한 삼바사용자염을 간단하게 확인
# smbstatus -u papa -> 특정 계정의 삼바사용자의 로그인 정보만을 확인

* 삼바서버의 NetBIOS 호스트 파일 lmhosts
이 파일의 주된 목적은 삼바에서 사용할 호스트명정보(주로 IP주소)를 쉽게 찾을 수 있돍 하기위한 것이다.
이 파일에 등록된 호스트정보를 이용하여 smbclient와 같은 삼바유틸리티에서 등록된 호스트의 삼바서버로 접속할 때에 호스트명만으로 사용할 수 있다.

* 삼바설정파일 점검하기 위한 testparm
사용형식 : testparm [-s] [-h] [-x] [-L <서버명>] 설정파일명 [호스트 IP]
# testparm
# testparm -l 192.168.0.100

* 삼바서버를 이용한 웹폴더 구축
- 웹폴더 사용을 위한 삼바서버 설정
# useradd dhan
# passwd dhan
Changing password for user dhan.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.
# smbpasswd -a dhan
New SMB password:
Retype new password:
Added user dhan.
# vi /etc/samba/smb.conf
[dhan]
comment dhan web folder
path = /home/dhan
valid users = dhan
read only = no
writable = yes
public = no
browseable = yes
printable = no
crate mask = 0750
# /etc/init.d/smb restart
- 개인 PC의 탐색기에서 운격삼바디스크 사용하기
- 웹브라우즈에서 웹폴더 형식으로 사용하기

***********************************************************
* DHCP 서버
***********************************************************

* 설치
- dhcp서버 rpm으로 설치 및 dhcp서비스 파일들
# wget http://ftp.superuser.co.kr/pub/redhat/9/RPMS/dhcp-3.0pl1-23.i386.rpm  DHCP서버와 중계 에이전트
# wget http://ftp.superuser.co.kr/pub/redhat/9/RPMS/dhcp-devel-3.0pl1-23.i386.rpm DHCP서버와 접속시키기 위한 개발용 헤더 파일들과 라이브러리들
# wget http://ftp.superuser.co.kr/pub/redhat/9/RPMS/dhclient-3.0pl1-23.i386.rpm  DHCP서버와 접속시키기 위한 클라이언트
# rpm -Uvh dhcp-3.0pl1-23.i386.rpm
# rpm -Uvh dhcp-devel-3.0pl1-23.i386.rpm
# rpm -Uvh dhclient-3.0pl1-23.i386.rpm
- dhcp 서버 소스로 컴파일하여 설치하기
1. dhcp 소스가져오기
# wget ftp://ftp.isc.org/isc/dcp/dhcp-3.0.1.tar.gz
2. dhcp 소스 압축풀기
# tar xvfz dhcp-3.0.1.tar.gz
3. configure하기
# ./configure
4. 컴파일하기(make)
# make
5. 컴파일된 파일 설치하기
# make install

* 환경파일들
- DHCP 데몬파일
  dhcp 서버데몬 : /usr/sbin/dhcpd
  dhcp 릴레이에이전트 데몬 : /usr/sbin/dhcrelay
- DHCP 메인설정파일
  dhcp서버의 설정파일 : /etc/dhcpd.conf
- DHCP IP할당로그파일
  dhcp IP할당ㄹ그파일 : /var/lib/dhcp/dhcpd.leases
- DHCP서버 실행과 종료 및 재시작
# /etc/init.d/dhcpd start
# /etc/init.d/dhcpd restart
# /etc/init.d/dhcpd stop

* DHCP 서버 설정파일 dhcpd.conf
이 파일은 /usr/share/doc/dhcp-3.0pl1/ 디렉토리에 존재하는 dhcpd.conf.sample 파일을
/etc/dhcpd.conf 파일로 복사하여 편집한다.
subnet 192.168.0.0 netmask 255.255.255.0 {
  -> 192.168.0.0/255.255.255.0 네트워크의 범위에 해당하는 IP주소 가운데 아래 reage항목에 나오는 바와 같이 192.168.0.128부터 192.168.0.255까지의 IP주소를 DHCP클라이언트 컴퓨터에 유동사설IP주소를 할당하기 위한 설정이다.

# -- default gateway
  option routers 192.168.0.1;
  -> DHCP클라이언트들이 사용할 기본게이트웨이이다.

  options subnet-mask 255.255.255.0;
  -> IP주소를 할당받은 DHCP클라이언트가 사용할 Subnet Mask를 의미한다.

  option nis-domain "domain.org";
  option domain-name "domain.org";
  -> DHCP 서버의 자체 도메인이름을 의미한다.

  option domain-name-servers 192.168.1.1;
  -> IP주소를 할당받은 DHCP클라이언트가 사용할 DNS서버를 설정한다.

  option time-offset -18000;
# option ntp-servers 19.168.1.1;
# option netbios-name-servers 192.168.1.1;
# - Select point-to-point node(default is hybrid). Don't change this unless you understand NetBIOS very well
# option netios-node-type 2;

  range dynamic-bootp 192.168.0.128 192.168.0.254;
  -> DHCP클라이언트에게 할당할 수 있는 IP주소의 범위이다.

  default-lease-time 21600;
  -> 할당시간이 설정되어 있지 않은 DHCP클라이언트가 할당받은 IP주소를 가지고 있을 수 있는 시간을 의미한다.

  max-lease-time 43200;
  -> 할당된 IP주소를 DHCP클라이언트가 임대할 수 있는 최대 시간을 의미한다.

  # We want the nameserver to apperar at a fixed address
  host ns {
    -> DHCP서버를 이용하여 통신을 하는 많은 DHCP클라이언트들 가운데 특정 호스트에게만 항상 고정IP를 사용할 수 있도록 하기 위한 설정이다.

    next-server marvin.redhat.com;
    hardware ethernet 12:34:56:78:AB:CD;
    -> 고정IP주소를 할당할 DHCP클라이언트의 MAC주소를 설정한다.
    fixed-address 207.176.4.254;
    -> 위의 MAC주소를 가진 DHCP클라이언트에게 할당할 고정 IP주소
  }
}

* DHCP서버의 네트워크 설정
DHCP서버로 사용되기 위해서는 DHCP서버의 ethernet에 멀티캐스트가 가능하도록 설정되어 있어야 한다.

* DHCP서버를 사용하는 DHCP클라이언트 설정
- 리눅스에서 자동 IP할당 받기 위한 DHCP 클라이언트 설정
# cat /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0
ONBOOT=yes
BOOTPROTO=dhcp
# /etc/init.d/network restart
- 윈도우에서 자동 IP할당받기 위한 DHCP클라이언트 설정

***********************************************************
* PROXY 서버
***********************************************************


***********************************************************
* MRTG 트랙픽서버
***********************************************************

* SNMP
# wget http://ftp.superuser.co.kr/pub/redhat/9/RPMS/net-snmp-5.0.6-17.i386.rpm
# rpm -Uvh net-snmp-5.0.6-17.i386.rpm
# /etc/init.d/snmpd start
SNMP패키지를 설치하면 /etc/snmp/snmpd.conf 라는 SNMP설정파일이 있다.
이 설정파일에서 SNMP커뮤니티네임(community name)과 시큐리티네임(security name)을 각각 설정한다.
com2sec notConfigUser default bible
group notConfigGroup v1 bible
group notConfigGroup vc bible

* MRTG 트랙픽서버 구축
- 필요한 프로그램들 설치하기
  perl
  zlib
  libpng
  freetype
  jpeg
  gd
  apache
- MRTG 설치
# wget ftp://ftp.ntua.gr/pub/net/monitoring/mrtg/mrtg-2.11.1.tar.gz
# tar xvf mrtg-2.11.1.tar.gz
# cd mrtg-2.11.1
# ./configure --with-gd=/usr/local/gd --wit-z=/usr/local/zlib --wit-png=/usr/local/libpng
# make
# make install
# cd /usr/local/mrtg
# mkdir conf
- MRTG설정파일 생성하기
# CFGMAKE --GLOBAL 'WorkDir: /home/mrtg/www/kebia_1' --global 'Options[_]: bits,growright' --output /home/mrtg/conf/bible.cfg bible@192.168.0.5
  WorkDir: 옵션은 MRTG의 실행결과 파일들을 저장할 위치를 지정
  Options에 설정한 부분은 그래프의 단위와 어느쪽으로 그릴 것인가를 지정
  /home/mrtg/conf/bible.cfg는 생성될 결과파일을 지정
  bible@192.168.0.5에서 192.168.0.5는 MRTG의 분석대상이며 bible은 대상서버에 설치된 snmp의 커뮤니티네임(community name)이다.
- cfg파일 분석
- MRTG 실행
# /usr/local/mrtg/bin/mrtg /home/mrtg/conf/kebia_1.cfg

<<Scrap url : http://gilgal.co.kr>>

2007/07/04 21:21 2007/07/04 21:21
이 글에는 트랙백을 보낼 수 없습니다
웅쓰:웅자의 상상플러스
웅자의 상상플러스
전체 (379)
게임 (5)
영화 (2)
기타 (23)
맛집 (5)
영어 (2)
대수학 (3)
형태소 (5)
Hacking (9)
Linux (112)
HTML (48)
Application_developing (48)
Web_developing (102)
Window (11)
«   2007/07   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        
  1. 2016/01 (1)
  2. 2015/12 (3)
  3. 2015/10 (3)
  4. 2015/03 (2)
  5. 2015/01 (4)