perl에 관하여 논문 형식으로 작성한 것이다.

 

대표적인 짜집기 논문이다 -0-;; 이러면 안되는데.. 후

 

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

 

문자열 처리 전문가 Perl


오치환*

*동명정보대학교 컴퓨터공학과


Specialist Perl at String Handling


Chy-Hwan Oh*

*Department of Computer Engineering, Tong-Myong University, Busan , Korea


요    약 : 본 논문에서는 Perl에 관하여 논한다. 특히 Perl은 문자열을 다루는데 최고의 언어라고 할수 있다. Perl에 관하여 알아보고, 사용 되는 분야에 대해서 논할 것이다. 그 중 문자열 처리에 관한 부분에 집중하여 논한다.


핵심용어 : 문자열 처리, perl


ABSTRACT :  In this paper, We describe about Perl. Especially perl is the best language for string handling. We inquire out perl and talk about where it using. Among them, We describe about String Handling


KEY WORDS : String handling, perl

1.서론

 Perl(Practical Extraction and Reporting Language)은 임의의 형태를 갖춘 텍스트 파일을 읽고, 이 파일에서 의미있는 정보를 추출하여 이 정보를 근거한 레포트를 출력하는 제반 작업에 최적화된 언어이며, 시스템 관리에도 매우 적절한 언어이다.

또 perl은 아름다운 언어보다는 실용적인 언어를 지향한다. perl은 (이 언어의 개발자가 말하기를) C, sed, awk 그리고 sh와 같은 언어나 프로그램들의 가장 좋은점들을 취합한 언어이므로, 이 언어, 프로그램들에 익숙한 사람들이라면 perl 사용에도 역시 큰 어려움을 느끼지 않을 것이다. (컴퓨터언어 사학자라면 perl의 문법이 아마 csh이나 Pascal 혹은 BASIC-PLUS와도 닮았다고 생각할 것 같다.)

문장구조는 C 언어와 상당히 흡사하다. 대개의 Unix용 프로그램들과는 달리, Perl은 우리가 사용할 데이터 크기에 한계를 두지 않는다. 메모리만 충분하다면 한 파일의 내용 전체를 변수하나에 넣을수 있다.. 재귀(Recursion)는 무제한 반복될 수 있다. 해쉬(혹은 조합배열)에서 사용하는 테이블은 성능저하를 일으키지 않으면서 무한대로 커질 수 있다. Perl은 섬세한 패턴일치(pattern matching) 기술을 이용하여 많은 양의 데이터를 빠르게 검색할 수 있다. 비록 텍스트 검색에 최적화되어 있긴하지만 이진데이터도 다룰 수 있으며 해쉬와 같은 형태의 dbm 파일도 만들어낼 수 있다. 또 setuid를 사용하는 스크립트의 경우 C 프로그램에서 사용하는 것보다 안전한데, 데이터 흐름을 추적하는 메커니즘이 말도 않되는 대부분의 보안상의 빈틈을 막아주기 때문이다.


만일 sed나 awk, sh (혹은 bash)를 사용한 프로그램을 만들어 문제를 풀어 나갈 때, 그 문제가 이런 프로그램의 능력으로는 극복하기 어렵다거나, 혹은 프로그램이 좀 더 빨랐으면 하는데, C언어를 사용해서 어리석게도 시간낭비를 하고 싶지 않다면, Perl이 해결책을 줄 것이다. 참고로, sed나 awk 스크립트를 Perl 스크립트로 바꿔주는 변환기들을 (인터넷에서) 찾을 수 있다.

Perl은 더구나 HTML이나 사용자입력과 같은 텍스트를 다루는데 아주 강력한 기능을 가지고 있으므로 문자열 위주의 데이터를 다루기에는 최적의 언어라 할 수 있으며, UN*X, MVS, VMS, MS-DOS, Macintosh, OS/2, Amiga, Windows NT 등 여러 가지 Operating system에 Porting되어 있으므로 소스파일의 이식성이 뛰어나다.

또 Perl은 강력한 사용자층을 가지고 있다. GURU급의 사용자, 개발자로부터 초보자에 이르기까지 Perl의 확장과 사용에 많은 시간과 정렬을 제공하는 사용자들이 CPAN의 많은 모듈을 만들고, 사용하고 고쳐나가면서 Perl의 발전에 힘쓰고 있다. 이 개발자, 사용자층이 있는 이상, Perl은 계속해서 발전할 것이다.

C, C++같은 언어를 써보면 그 언어에서 문자열과 메모리의 관리가 얼마나 짜증나는 일인지 안다. Perl은 그런 복잡한 것이 없다. 즉, 언어의 관리 보다는, 언어의 유용성에 초점을 맞춘 언어이다.

Perl은 무료이다. Perl을 빛내주는 모듈들도 99.9999%가 무료이다. 그리고 많은 프로그램들이 무료로 제공되고 있다.


1993년부터 시작해서 Perl 버젼 5는 거의 완전히 새로 만들어졌고, 그로 인하여 다음과 같은 내용들이 추가되었다.

- 수많은 모듈들을 이용한 모듈화(modularity)와 재사용성(reusability) : 이는 라이브러리를 좀 더 효율적으로 사용할 수 있는 방법이다.

- C 언어의 한부분으로 사용하거나, C 언어를 이용하여 확장 가능.(embeddable and extensible)

- 다른 데이터(DBM같은)를 변수에 통합하여 변수를 조작하는 것으로 원래의 데이터를 조작할 수 있는 기능. (roll-your-own magic variables (including multiple simultaneous DBM implementations))

- 서브루틴으로 내장 함수 재선언(override)하기. 서브루틴의 동적인 선언(autoload). 프로토타이핑.

- 단순한 scalar, 배열, 해쉬이상의 복잡한 데이터 구조와 익명의 함수. (arbitrarily nested data structures and anonymous functions)

- 객체지향적 프로그래밍(object-oriented programming)

- C 코드 또는 Perl 바이트코드로의 변환(compilability into C code or Perl bytecode)

- 쓰레드 지원(support for light-weight processes (threads))

- 국제화, 지역화, 유니코드(support for internationalization, localization, and Unicode)

- 변수의 사전적 범위지정(lexical scoping) - my 연산자를 이용한 변수의 접근범위 제한.

- 향상된 정규식(regular expression enhancements)

- 향상된 디버거와 내장 편집기를 사용하여 사용자와 상호작용 하는 환경 (enhanced debugger and interactive Perl environment with integrated editor support)

- POSIX 1003.1 호환 라이브러리(POSIX 1003.1 compliant library)


2. 사용분야

perl은 한 때 HTTP, HTML이 부상하면서 CGI를 위한 언어로 각광 받았다. 하지만, CGI로서의 역할은 PHP와 같은 언어나, JSP, ASP 등의 등장으로 미미해진다.

그러나, 웹의 부상과 CGI로서의 역할이라는 다소 표면적이고 번지르르한 허명뒤에는 또 다른 역할이 있었다.(물론 아직도 꼭 CGI가 필요한 곳에서는 막강한 힘을 발휘하고 있다. Apache 서버의 mod_perl이 바로 그 중 하나일 것이다.)

그 것이 바로 "관리"를 위한 역할이다. 관리는 시스템 관리만을 위한 것이 아니다. 시스템 관리는 물론이고, 데이터 관리, 웹 관련 관리 등 다양한 관리를 포함한다.


웹 관련 관리에 관해 간단히 말해 보자면, 실제적인 사용자와의 인터페이스는 다른 언어에 맡긴다지만, 가장 쉽게 생각할 수 있는 perl의 또 다른 역할이 웹사이트의 방문기록을 분석하는 것이다. 웹의 방문기록을 counter 프로그램을 이용하여 저장한다면, 그것은 약간 비효율적이다. 그 보다는 Log 파일에 저장된 내용을 추후에 분석하는 것이 대개는 좋다. 이럴 때 perl을 사용하면 일이 매우 쉬워진다. 다른 언어로 하기는 비효율적일 것이다. 왜냐하면 문자열을 다루는 데에는 perl이 최고이기 때문이다. 또, 만일 어떤 페이지의 내용이 30분이나 1시간에 한 번씩 바뀌어야 한다면(날씨관련 사이트처럼), 혹은 하루에 1, 2번 바뀌어야 한다면, perl을 이용하는 것이 가장 좋을 것이다. 주기적으로 어떤 조건에 따라서 새로운 페이지를 생성하여 HTML로 저장한 다음 사용하는 것이, 매번 php나 jsp를 이용하여 생성하는 것보다는 훨씬 경제적이고 속도도 많이 빠를 것이다. 서버가 아닌 클라이언트의 입장이 될 수도 있다. 어떤 페이지를 주기적으로 다운로드 받아서 분석한 후 데이터로 저장해야 한다면, 최고의 문자열처리 언어인 perl을 이용하는 것이 좋을 것이다. 게다가 대상 페이지가 자주 변하는 것이라면 융통성있는 perl이 막강한 힘을 부여해 줄 것이다.


시스템 개발에서도 prototype 언어로 활용할 수도 있고(예를 들어 메일 서버를 구현하기 전에 perl로 실제로 구현해 보고, 이를 C와 같은 언어로 porting 하는 경우), 다른 언어의 code 생성에 template 기반의 tool로 사용할 수도 있는 등 그 쓰임새가 엄청나다.


3. Perl as language

본 장에서는 perl 의 기초문법에 대해 알아본다.


3.1. 펄의 데이터 타입 및 특수변수

3.1.1 Perl

1986년 Unix 프로그래머인 Larry Wall이라는 사람이 자신이 맡은 업무를 보다 쉽게 처리할려고 하는 과정에서, 자신의 일을 해결할수 있는 가장 간결하고 적합한 유틸리티를 만들게 되었다, 그래서 그는 새로운 프로그래밍 언어를 발명하게 되었고, 그후 펄이란 이름으로 빠르게 번져나갔다고 한다.


3.1.2 Date Type

펄의 데이터 타입은 스칼라(scalars), 배열(arrays), 결합형 배열(associative arrays) 이렇게 세가지가 있다.

그리고, 모든 변수명을 대소문자를 구별한다. $temp와 $Temp는 서로 다른 변수가 된다.


3.1.2.1 스칼라(Scalars)

펄은 다른 언어와는 달리 모든 수와 문자열을 이 스칼라로 표현한다.

스칼라 변수명은 '$'표로 시작한다.


    $a = 2 ; - 숫자

    $b = "test" ; - 문자

    $temp = "My name is Soo" ; - 문자열


3.1.2.2 배열(Array)

스칼라의 집합을 배열이라 하며, '@'표시로 시작한다. 다음과 같이 괄호안에 ',(쉽표)'로 각 변수는 구별된다.


    @name = ("chanho", "unicorn", "jongbum", "Hongsun") ;

    @id = (1234, "unisoo", 0987) ;


또한 배열안에 또 다른 배열이 정의 될수 있다.


    @a = (1, 2) ;

    @b = (3, 4) ;

    @c = (@a, @b) ;

   

이렇게 하면 @c에는 1, 2, 3, 4의 값을 가지게 된다.

여기서 주의할 것은 배열을 스칼라에 할당 했을 경우이다.


    @a = (1, 2) ;

    @b = @a ; <-- @b는 당연히 1, 2의 값을 가지게 된다.

    $c = @a ; <-- $c는 1, 2 의 값이 아닌 2의 값을 가지게 된다.

    $c는 array내의 값을 가지는 것이 아니라, array 내의 값들의 개수를 가진다.

만일 배열내의 임의의 값에 접근하고자 할경우는,


    @a = ("name", "school", 4, "Korea") ;

    $temp1 = $a[0] <-- name

    $temp2 = $a[3] <-- Korea 의 값을 가지게 된다.


배열은 0번째부터 값을 가지게 된다. 그러니깐 위 경우는 값이 4개이므로 $a[0]은 'name'이 되고, $a[1]은 'school'이 되고, $a[2]는 '4'가 되고, $a[3]은 'Korea'의 값을 가지게 된다.


3.1.2.3 결합형 배열(Associative Arrays)

이 결합형 배열은 위의 배열과는 조금 다른 모습을 가진다. 위의 배열은 그냥 값만 가지는것에 비해, 결합형 배열은 키(key)와 이 키에 대응하는 값을 가진다. 결합형 배열은 '%'표시로 시작되며, 다음과 같다.


    %animal = ("name", "ppoby", "number", "two", "race", "jabjong") ;


이렇게 표현되어 지며, 배열의 각 요소들에 접근하기 위해서는


    $temp = $animal{"name"} ; <-- 'ppoby'

    $temp_1 = $animal{"number"} ; <-- 'two'

    $temp_2 = $animal{"race"} ; <-- 'jabjong' 의 값이 할당되어 진다.

즉 위의 예에서, 'name은 ppoby를 number는 two를 race는 jabjong' 과 쌍을 이루게 된다. 다시 말해서 위의 배열에서 키는 'name, number, race'가 되고, 각 키의 값으로는 'ppoby, two, jabjong'이 되는 것이다.


3.1.3 특수 변수(Special Variables)

여기서는 펄에서 대단히 많이 쓰이는 변수 몇가지만 소개하기로 한다.


3.1.3.1 환경 변수(Environment Variables) - %ENV

이 변수는 환경 변수를 가지고 있는 결합 배열이다. 이 변수를 이용해서 아주 쉽게 환경 변수의 내용을 얻을 수 있다. 폼으로 부터 보내어진 데이터나, 접속한 사용자의 브라우져 정보, 클라이언트의 IP 주소, 자신의 서버가 사용하고 있는 웹 프로토콜의 버젼 등 쉽게 그 정보를 얻을수 있다.

사용 방법은


    $ENV{'원하는 환경변수'} ;

    $temp = $ENV{'REMOTE_ADDR'} ; 접속한 사람의 IP주소를 알수 있다.

자주 쓰이는 변수를 정리해 보았다.

CONTENT_LENGTH

 폼과 함께 제출된 데이터 내용의 바이트 수

CONTENT_TYPE

 폼과 함께 제출된 데이터 내용의 타입

HTTP_USER_AGENT

 웹 브라우져 소프트웨의 버젼

PATH_INFO

 요청과 함께 웹 서버로 보내어진 경로

QUERY_STRING

 폼 제출로 부터 데이터를 담고 있는 문자열

REMOTE_ADDR

 클라이언트의 IP주소

REMOTE_HOST

 클라이언트의 호스트 이름

REQUEST_METHOD

 스크립트를 호출하는 방법(GET, POST)


3.1.3.2 프로그램 인자 - @ARGV

펄 명령 라인 내에서 지정된 인자들은 특수 배열인 @ARGV를 통해 펄 스크립트 내로 전달 되어 진다.


3.1.3.3 현재 라인 - $_

특수 변수 '$_'는 입력의 현재 라인을 저장한다.


3.1.3.4 $(숫자) - $1, $2, $3 .....

특수 변수 $n는 패턴에 의해 매칭된 변수를 저장할 때 사용 되어진다. 첫번째 매칭된 문자열은 $1에, 두번째 매칭된 문자열은 $2에... n 번째 매칭된 문자열은 $n 에 기록 된다.


3.2. 제어문, 파일제어

3.2.1 연산자(Operator)

우선 Perl에서 쓰이는 대표적인 논리적 연산자에 대해서 알아보겠다. 논리적 연산자로는 ||(or) 와 &&(and)가 있다.

이 두개의 연산자는 양쪽에 오는 값에 따라 참, 또는 거짓의 값을 되돌린다.

||(or)

|| 연산자는 '또는' 이란 뜻으로, 'A || B' 일 경우 'A 또는 B 가 ...' 라는 뜻이 되며, A,B 두 값중 하나만 참이 되더라도 전체의 값은 참이 된다. 두개의 값이 모두 거짓일 경우 전체의 값이 거짓이 된다.

&&(and)

&& 연산자는 '그리고' 이란 뜻으로, 'A && B' 일 경우 'A 그리고 B 가 ...' 라는 뜻이 되며, A,B 두 값중 하나만 거짓이 되더라도 전체의 값은 거짓이 된다. 두개의 값이 모두 참일 경우 전체의 값이 참이 된다.

그리고 이 두개의 연산자는 이런 비교의 경우가 아닌, 펄 문장들을 결합시키는 역할 또한 합니다. 이때 '||'연산자는 '그렇지 않다면..' 이 되고 '&&' 연산자는 '그렇다면..' 으로 해석될수 있다.

    $value > 5 || print "값이 너무 작습니다.";

    $value < 5 && print "값이 너무 작습니다.";

   

이 두 문장 모두 같은 역할을 하는데,

위의 예는 'value의 값이 5보다 크지 않다면...' 이 되고,

아래의 예는 'value의 값이 5보다 작다면....' 이 된다.


3.2.2 제어문(Flow Control)

Perl 에서 쓰이는 대표적인 제어문은 while, foreach, if등이 있고, 그 외에 until, for, unless등이 있다.


3.2.2.1 while

while()은 ()내의 조건문이 거짓이 될때 까지 문장을 반복 수행한다.


    $n=100;

    while ($n > 0) {

        print "$n\n";

    }


3.2.2.2 foreach

foreach 문은 배열을 처리할때, 아주 유용하다. foreach 다음에 오는 배열이 널값(아무런 값도 없을때)일때 까지 문장을 수행한다.


    @name = ("Kim", "young", "soo");

    foreach $temp(@name) {

        print "$temp\n";

    }


위 문장에서 $temp 변수는 처음에는 'kim'의 값을 가지며, 두번째는 'young'의 값을, 세번째는 'soo'의 값을 가지며, 네번째에서 $temp는 널값을 가지게 되므로 루프를 종료하게 된다.

@name 배열 값의 개수가 1000개라 하더라도, 위 3줄의 예로써 모든 @name 의 값을 출력할수 있다.



3.2.2.3 if... elsif.... else

if 문은 '만약 조건문이 참이라면' 의 뜻이 된다.

if... elseif.... else 가 모두 쓰인 상태라면, if(만약 이것이 참이라면...) elsif(그렇지 않다면....) else(위의 두 개 모두 아니라면...) 이런 식으로 쓰인다.


    $temp = 5;

    if ($temp == 5) { print "temp is $temp"; }

    elsif ($temp == 3) { print "temp is $temp"; }

    else { print "Not found temp"; }


3.2.2.4 조건부 표현 (Conditional Expression)

그럼 여기서 펄에서 쓰이는 관계형 연산자에 대해서 알아보겠다.


    같 음                ==  eq

    다 름                !=  ne

    부호를 가지는 다름  <=>  cmp

    보다 큼              >   gt

    보다 크거나 같음     >=  ge

    보다 작음            <   lt

    보다 작거나 같음     <=  le


3.2.3 파일 제어

여기서는 펄에서 파일의 접근에 대한 방법을 알아 보겠다. 펄에서 파일을 열기 위해 쓰는 명령어는 open()이 있다. 물론 사용하신 다음에는 close()로 닫아야한다. 여기선 텍스트 파일에 대한 이야기를 하겠지만, dbm 파일을 이용할수 있는 dbmopen()과 dbmclose()도 있다.


3.2.3.1 open(), close()

 open() 함수는 새로운 파일 핸들을 어떤 파일에 연계시키기 위해서 사용한다.


 open(FILE, "> /etc/guest.txt");

 close(FILE);

 open(FILE, ">> /etc/file.log");

 close(FILE);


이렇게 사용 될수 있다. 위 예에서 'FILE'은 새로운 파일 핸들이고, '.../filename'은 사용 할려고 하는 파일명이다.

그리고 `` '' 안의 파일명 앞에 여러가지 기호가 쓰이는데, 이것은 파일에 접근 방법을 지정할수 있다.


    '<'   읽기를 위한 파일 열기(기본값)

    '>'   쓰기를 위한 파일 열기

    '>>'  이어쓰기를 위한 파일 열기

    '+C<' 읽기와 쓰기를 위한 파일 열기

    '+>'  읽기와 쓰기를 위한 파일 열기

    '|'   (파일 이름 앞) 파일을 명령(펄에서 파일로 전송)처럼 다룬다

    '|'   (파일 이름 뒤) 파일을 명령(파일에서 펄로 전송)처럼 다룬다


open() 함수로 파일을 열었으면, 반드시 close() 함수로 닫아야 한다. 그리고 open (파일핸들, ``파일제어기호 파일명(경로포함)''); 에서 파일제어기호 와 파일명에는 콤마등의 기호가 없다.



3.3. 정규표현, 패턴매칭

3.3.1 정규 표현(Regular expression)

정규 표현(regular expression)은 일반화된 문자열을 설명하기 위한 규칙의 집합이다.

어떤 문자열이 정규 표현의 규칙을 따르고 있다면, 그 정규 표현은 그 문자열을 매치(match)한다고 한다.

예를 들면, 정규표현 'b.'는 문자열 'observe, body, abc'와는 매치를 이루고, 'b, Bell, Bob' 와는 매치하지 않다. 이 표현 'b.'는 '소문자 b가 문자열 내에 있어야 하며, 다른 문자가 뒤에 한 개 이상 있어야 하기(단,\n는 예외)' 때문이다.

펄에서는 많은 정규 표현들이 있는데, 그것들을 정리해 보았다.


3.3.1.1 정규표현 어써션(Assertions)


    기호   매치 영역            매치      매치함    매치안

    ----------------------------------------------------------------------------        '^'    문자열의 시작부분   ^fool      foolish   tomfoolery

    '$'    문자열의 끝 부분     fool$      April    fool foolish

    '\b'   단어 영역            be\bside    be       side beside

    '\B'   비단어 영역        be\Bside   beside    be side


3.3.1.2 정규표현 아톰 퀀티파이어(Quantifiers)


    기호      매치 영역           예     매치함    매치안함

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

    '*'     없거나 하나 이상     ab*c    ac, abc       abb

    '+'       하나 이상         ab+c     abc         ac

    '?'       없거나 하나         ab?c      ac, abc       abbc

    '{n}'     n개 만큼           ab{2}c      abbc      abbbc

    '{n, }'   적어도 n개 만큼   ab{2,}c    abbc, abbbc   abc

    '{nm}' 적어도 n개, 많아야 m개 ab{2,3}c   abbc, abbbc   abbbbc


3.3.1.4 특수문자(Special Characters)


    기호   매치 영역                예     매치함 매치안함

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

    '\d'   숫자                     b\dd   b4d    bab

    '\D'   비숫자                   b\Dd   bad    b4d

    '\n'   새 라인(줄바꿈)      

    '\r'   캐리지 리턴      

    '\t'   탭      

    '\f'   폼 피드      

    '\s'   공백 문자      

    '\S'   비공백 문자      

    '\w'   알파벳이나 숫자                 a\wb   a2b    a^b

    '\W' 알파벳이나 숫자를 제외한 문자  a\Wb   aa^b   aabb


3.3.2 매 칭(Matching)

위의 표현들은 펄 프로그래밍시 매우 많이, 그리고 매우 유용하게 쓰인다. 보통 매치 연산자는 두개의 '/'로 둘러싸인 정규표현으로 구성된다. 그리고 특수연산자 '=~'는 첫번째 값과 두번째 값이 매치하는지에 따라 참과 거짓으로 평가 한다.


    예) $filename =~ /dat$/ && die "Can't use. dat files. \n";

   

이 예의 뜻은 만약 $filename의 끝 문자열이 'dat'로 끝나지 않는다면 'die ~ '해라라는 뜻이 된다.


연산자 '!~'는 '=~'의 반대개념이다.


    예) $ENV{'PATH'} !~ /perl/ && warn "Not sure if perl is in your path...";

   

perl의 경로가 $ENV{'PATH'}와 매치되지 않는다면.... 의 뜻이 된다.


3.3.3 대 치(Substitution)

대치는 쉬우면서 강력한 기능을 제공한다. 대치 연산자는 '/.../.../' 를 사용한다. 첫번째와 두번째 '/'에 대치될 패턴이 오게되고, 두번째와 세번째 '/'에 대치할 패턴이 오게 된다.


    예)$name = "Kimyoungsoo";

       $name =~ s/Kim/Unicorn/;

       $name의 'Kim'을 'Unicorn'으로 대치하게 된다.


4. 문자열 처리에 관한 Perl 사용 예제

본 장에서는 perl을 이용 하여 문자열을 처리하는 방법들에 대해서 자세히 알아 본다.


4.1. 다른 프로그램을 실행시켜 출력내용 읽기

다른 프로그램을 실행시키고 그 출력 내용을 읽는 방법은 여러가지가 있다.


4.1.1. Backtick


역따옴표(`...`)는 그 안에 들어있는 내용을 shell을 통해 실행하고, 표준출력(STDOUT)으로 출력되는 내용을 반환한다. 따라서,


$content = `cat /etc/passwd`;


는 passwd 파일을 간단히 읽어들인다.

물론, 단순한 파일을 읽는데는 직접 open을 해서 읽어들이는 것이 좋다. 역따옴표를 사용하게 되면, 그 내부의 프로그램을 실행하기 위해서 별도의 프로세스가 생성되어야 하기 때문에 그만큼 시스템의 자원을 소모하게된다.


4.1.2. open 함수를 이용하는 방법


open 함수에서 파일이름분에 > 대신 | (파이프)기호를 뒤에 붙이면 해당 파일이름을 실행시키고 그 출력내용을 읽을 수 있다.


#!/usr/bin/perl

# prog.pl

print "$$\n";

warn "Exiting\n";



#!/usr/bin/perl

# opener.pl

$p = open(RUN, "./prog.pl |") || die $!;

@out = <RUN>;

close RUN;


print "opener: $p\n";

print map { "opener: $_" } @out;


opener.pl에서 prog.pl을 "./prog.pl |"와 같이 하여 열었다.(open)

그 후 일반 파일 읽기에서 하는 것처럼 <RUN>를 이용하여 출력내용을 읽는다. 참고로, open 함수가 반환하는 것은, 프로그램이 제대로 실행되었을 때, 새로운 프로세스의 프로세스 아이디이다.

만일 이 프로그램이 오랜시간동안 살아 있는 것(웹서버처럼)이라면 close를 하지 않을 경우 문제가 될 수 있다. 예를 들어, open은 계속해야 하는데, close를 해 주지 않는다면, 해당 프로세스가 죽지않고 계속 남아서 시스템자원을 차지하고 있어서 결국 시스템이 멈추는 상황이 발생할 수도 있게된다.

따라서 반드시 close를 해 주어야 합니다.

만일 표준에러(STDERR)까지 같이 읽어야 한다면


#!/usr/bin/perl

# prog.pl

print "$$\n";

warn "Exiting\n";



#!/usr/bin/perl

# opener.pl

$p = open(RUN, "./prog.pl 2>&1 |") || die $!;

@out = <RUN>;

close RUN; #


print "opener: $p\n";

print map { "opener: $_" } @out;


와 같이 2>&1를 추가합니다.


4.2. 문자열 안에 실행문 넣기

문자열 내에 실행문을 직접 삽입할 수 있다.

보통의 print 문에서는

print "수익대 비용 차액 : " . ($income - $outcome) . "\n";


와 같은 식이다.

만일 print문 안에 이런 계산식이 많이 들어가야 한다면, 조금 귀찮을 수도 있다. 이런 경우 문자열 내에 계산식을 직접 삽입하는 것이 간편하겠다고 생각되면 다음과 같은 형식을 사용한다.

print "수익대 비용 차액 : ${ \($income - $outcome) }\n";


문자열 내에서 ${스칼라변수리퍼런스}는 그 리퍼런스가 가리키는 변수의 값을 나타낸다. 예를 들면 다음과 같은 경우이다.

$var = 365;

$rvar = \$var;

print "var = ${$rvar}\n"; # 이 경우라면 "var = $$rvar\n"도 같은 결과이지만, 설명을 위해 위 ${ \($income - $outcome) }는 ${ }의 안에 \($income - $outcome)을 가지고 있다. 목록(list)을 나타내는 괄호앞에 \(백슬래쉬)를 두면 목록의 각 요소에 대해 리퍼런스를 만드는 것과 같다. 그래서

$what = \( $a, $b, $c )

$what = ( \$a, \$b, \$c );

와 같게 되고, 스칼라 문맥에서 목록이 사용되면 맨 뒤의 것만 사용되게 되어 맨 마지막 요소인 $income - $outcome의 결과 값에 대한 리퍼런스가 ${ }와 함께 사용되어 계산값이 출력 되는 것이다. 맨 마지막 값이 사용되는 것을 직접 확인하려면 괄호의 앞부분에 다른 값을 넣고 컴마로 구분해 보면 된다.

print "수익대 비용 비율과 차액 : ${ \($outcome / $income, $income - $outcome) }\n";


이 경우 $outcome / $income 부분은 사라져 알 수 없다.

배열 역시 문자열 안에 넣을 수 있다. 문자열 내에서 배열리퍼런스의 사용은 @{배열의리퍼런스}의 형식이다. 물론 "@$array_ref"와 같은 맥락이다.

print "수익대 비용 비율과 차액 : @{ [ $outcome / $income, $income - $outcome ] }\n";


이 경우에는 \() 대신 []를 사용한다. []는 이름없는 배열(anonymous array)의 레퍼런스를 나타낸다.


4.3.  첫 글자는 대문자로, 나머지는 소문자로 바꾸기

영문 문자열의 첫글자만 대문자로 만들고, 나머지는 소문자로 만드는 법이다. 사용자의 입력을 받았을 때, 다양한 사용자의 입력 습관을 무시하고, 적절히 바꾸는 방법중 하나라고 생각된다.

쌍따옴표를 사용할 때, \n이 개행문자(new line)로, \t가 탭문자로 사용된다는 것을 알 것이다. 이 외에도 다양한 특수문자들이 있다. 그 중에는

\U : 문자열의 끝 또는 \E가 나올 때 까지 모든 문자를 대문자로 바꿈

\L : 문자열의 끝 또는 \E가 나올 때 까지 모든 문자를 소문자로 바꿈

\u : 바로 다음의 문자만을 대문자로 바꿈

\l : 바로 다음의 문자만을 소문자로 바꿈

과 같은 특수문자들이 있다. 이 특수문자들을 이용하면, 원하는 바를 이룰 수 있다.

$str = "what A wAnderful Day!";

$badstr  = "\u$str";   # What A wAnderful Day!

$goodstr = "\u\L$str"; # What a wanderful day!


$badstr의 경우는 굳이 설명이 필요없지만, 자칫하면 위와 같이 생각할 수 있다. (사실 프로그래머는 사용자의 입력을 ``예단''하는 수가 상당히 많다)

$goodstr은 일단 \L로 모든 문자들을 소문자로 바꾼후에 \u로 첫 문자만 대문자로 바꿔주었다.


4.4. 문자열의 맨 처음과 맨 뒤의 공백 없애기

파일에서 텍스트를 읽어 들이거나 사용자의 입력을 받을 때 때로 반드시 처리해 주어야 하는 것이 문자열의 맨 앞이나 맨 뒤의 공백문자들을 지우는 것이다. 다음의

$str =~ s/^\s+|\s+$//g;


한 줄이면 맨 앞의 공백과 맨 뒤의 공백을 한 번에 없앨 수 있다. 물론 습관에 따라

$str =~ s/^\s+//;

$str =~ s/\s+$//;


해도 무관하다.


4.5. 한글을 깨끗하게 자르기

어떤 데이터를 받거나 얻은 후, 데이터베이스에 넣거나 웹상에 표출할 때, 그 것이 너무 길어서 데이터베이스에서 받아들이지 않거나, 웹 페이지의 모양이 이상한 경우가 있다.

이 때는 받은 데이터의 일부를 불가피하게 잘라내야 하는 데, 영문이라면 큰 문제가 없지만 한글의 경우에는 문제가 된다.

이유는, 한글 한 글자를 나타내는 데 2바이트를 사용하기 때문이다.

예를 들어 "우리나라 만세"를 10 바이트로 잘라내려면 "우리나라" 8바이트, 공백 1바이트, '만'자의 앞부분 1바이트가 남아서 출력될 때는 깨진 글자가 보여지게 된다.

이 때는 다음과 같은 코드를 사용하시면 된다.


if( length($text) > 60 ) {

      $text = substr($text,0,60);

      $text =~ s/(([\x80-\xff].)*)[\x80-\xff]?$/$1/;

}


일단 문자열을 원하는 길이만큼만 남기고 잘라낸다(substr).

남은 문자열의 끝부분에 대해서 정규식을 적용하는데, 한글의 첫 바이트는 항상 \x7f 보다 크므로 [\x80-\xff]., 즉 한글의 첫바이트일 수 있는 [\x80-\xff] 한 바이트와 '.' 한 바이트가 합쳐진 것을 '*' 하면 한글 0 또는 그 이상의 글자가 된다. 그리고 한글의 첫바이트인 [\x80-\xff] 하나만 남는다면 온전한 한글 문자열부분인 (([\x80-\xff].)*) 부분이 $1 이라는 변수에 남아있으므로 그것을 일치된 전체 텍스트에 대해서 치환한다.


4.6. 날짜 패턴검사와 후참조(Backreference)

날짜 입력값의 패턴 검사를 해 보겠다.

날짜 입력값이 yyyy-mm-dd나 yyyy/mm/dd는 인정하고 그 외의 패턴은 인정하지 않는다고 한다면 다음과 같이 간단한 정규식을 사용하면 된다.


if( $dateinput =~ m|^\d{4}([-/])\d{1,2}\1\d{1,2}$| ) {

      print "Good\n";

} else {

      print "Not good\n";

}


여기서 상세한 날짜 범위에 대한 검증은 생략하겠다.

얼핏생각하면 패턴 검사를


$dateinput =~ m|^\d{4}([-/])\d{1,2}[-/]\d{1,2}$|


와 같이 하면 될 것이라고 생각할 수 있다. 하지만, 이 경우는 yyyy-mm/dd나 yyyy/mm-dd도 옳은 것으로 인정해 주기 때문에, 썩 좋은 해법이라고 보기 어렵다. 그래서


$dateinput =~ m|^\d{4}-\d{1,2}-\d{1,2}$| or $dateinput =~ m|^\d{4}/\d{1,2}/\d{1,2}$|


라고 할 수도 있으나, 그 보다는 맨 위의


$dateinput =~ m|^\d{4}([-/])\d{1,2}\1\d{1,2}$|


가 훨씬 간결하다. \1은 후참조(backreference)라고 한다. 정규식 패턴 내에서 앞의 괄호가 일치할 때, 패턴 내부에서 다시 참조할 수 있도록 별도로 저장해둔 내용을 참조하는 것이다.

이 것은 패턴 외부에서 $1, $2, $3...를 사용하는 것과는 달리, 패턴 내부에서 사용한다. 그래서 년과 월의 사이에 사용되는 구분자가 똑같이 월과 일의 사이에서 사용된 경우에만 올바른 패턴으로 결정할 수 있게 된다.

후참조 역시 $1, $2, $3...와 같이 \1, \2, \3...로 사용된다. 만일 날짜 + 시간 패턴을 검사한다면, 다음과 같이 사용할 수 있다.


m|^\d{4}([-/])\d{1,2}\1\d{1,2} \d\d([-:])\d\d\2\d\d$|


5. 결  론

Perl 을 사용 하는 많은 사람들의 인식 속에는 perl 은 그저 인터넷 분야에서 널리 활용되고 있다. 그래서 “오직 CGI 만을 위한 언어다”라고 생각 할지도 모른다. 펄을 창시한 래리 월이 TMTOWTDI, 즉 “There is more than one way to do it" 이라는 슬로건을 내 걸었듯이, 인터넷 프로그래밍은 물론이고 유닉스 시스템 관리, 네트워크, 기타 컴퓨터 분야의 전반에 걸쳐 활용될 수 있는 Perl 의 풍부한 활용성을 알리고자 이렇게 이글을 쓰게 되었다. 특히 문자열 처리에 관한한 최고임을 자부하는 perl 을 사용자 입맛에 맞게 사용 하였으면 한다.


참고문헌


[1] 톰 크리스잔센, 네이던 토킹턴 저, 안계용 역, “Perl Cookbook" 2000.

[2] "http://www.perlmania.or.kr" 문서 모음 게시판

[3] “http://www.perl.or.kr” About perl, Tips 게시판

[4] http://www.softbrain.co.kr/perl/” Perl 이야기 게시판

[5] http://www.activestate.com/” 윈도우용 perl 자료

[6] http://perl.sshel.com/” Docs 게시판

[7] http://seoul.pm.org/

Posted by '김용환'
,

자주 쓰는 MySQL 명령어 정리

# root암호설정 - root로 로그인하여 해야함
% mysqladmin -u root password '변경암호'
% mysqladmin -u root -p기존암호 password '변경암호'


root암호변경설정
PLEASE REMEMBER TO SET A PASSWORD FOR THE MySQL root USER !
This is done with:
/usr/bin/mysqladmin -u root -p password 'new-password'
/usr/bin/mysqladmin -u root -h ns.moyiza.net -p password 'new-password'


DB작업
DB생성: mysql> create database DB명 ( or % mysqladmin -u root -p create DB명 )
DB삭제: mysql> drop database DB명
DB사용: mysql> use DB명 (엄밀히 말하자면, 사용할 'default database'를 선택하는 것이다.)
DB변경: mysql> alter database db명 DEFAULT CHARACTER SET charset (4.1이상에서만 available)

MySQL 연결
mysql -u 사용자 -p DB명 ( or % mysqladmin -u root -p drop DB명 )

데이터파일 실행(sql*loader기능)
mysql>load data infile "데이터파일" into table 테이블명 ;
데이터파일에서 컬럼구분은 탭문자, Null값은 /n로 입력
데이터파일의 위치는 /home/kang/load.txt 와 같이 절대경로로 지정할것.

질의 파일 실행
쉘프롬프트상에서
mysql -u 사용자 -p DB명 < 질의파일
or
mysql프롬프트상에서
mysql> source 질의파일

쉘프롬프트상에서 질의 실행
moyiza@nero board]$ mysql mysql -u root -pxxxx -e
>        "INSERT INTO db VALUES(
>        'localhost', 'aaa', 'aaa',
>        'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'Y')"


사용자 생성 & 사용자에게 DB할당
shell> mysql --user=root -p mysql

mysql> INSERT INTO user VALUES('localhost','사용자',PASSWORD('비밀번호'),'Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');
mysql> INSERT INTO user VALUES('%','사용자',PASSWORD('비밀번호'),'Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');

mysql> INSERT INTO db(Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv) VALUES ('localhost','DB명','사용자','Y','Y','Y','Y','Y','Y');
mysql> INSERT INTO db(Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv) VALUES('%','DB명','사용자','Y','Y','Y','Y','Y','Y');

mysql> FLUSH PRIVILEGES; (or shell prompt: mysqladmin -u root -pxxxx reload)

CASE 2: GRANT명령을 이용한 사용자 생성(이 방법이 권장된다)
kang이라는 DB를 만들고, 이 DB를 아래에서 나열된 권한을 가진 kang이라는 사용자를 생성
create database kang;
grant SELECT,INSERT,UPDATE,DELETE,CREATE,DROP on kang.* to kang@localhost identified by 'kang';
grant SELECT,INSERT,UPDATE,DELETE,CREATE,DROP on kang.* to kang@'%' identified by 'kang';

mysql> create database kang;
Query OK, 1 row affected (0.00 sec)

mysql> grant SELECT,INSERT,UPDATE,DELETE,CREATE,DROP on kang.* to kang@localhost identified by 'kang';
Query OK, 0 rows affected (0.00 sec)

mysql> grant SELECT,INSERT,UPDATE,DELETE,CREATE,DROP on kang.* to kang@'%' identified by 'kang';
Query OK, 0 rows affected (0.01 sec)

mysql>

여러가지 명령정리
mysql> show variables;                               서버의 variables(설정사항)출력
mysql> show variables like 'have_inno%'                   조건에 맞는 variables만 출력
mysql> show databases;                               database목록
mysql> show tables;                                 현재DB의 테이블목록(temporary table은 출력하지 않음)
mysql> show tables from db명;                          지정된 db명이 소유한 테이블목록
mysql> show tables like 'mem%';                         조건에 맞는 테이블목록만 출력
mysql> show index from 테이블명;                        인덱스 보기
mysql> show columns from 테이블명;                       테이블구조(describe 테이블명, explain 테이블명)
mysql> show table status;                             현재 DB의 테이블들의 상태(row수,table type,row길이,..)
mysql> show table status from db명;                      지정된 DB의 테이블들의 상태(row수,table type,row길이,..)
mysql> show create table 테이블명;                       해당 테이블 생성 SQL문 출력
mysql> rename table 테이블1 to 테이블2;                   테이블명 변경(ALTER TABLE 테이블1 RENAME TO 테이블2)
mysql> rename table 테이블1 to 테이블2, 테이블3 to 테이블4;      rename multiple tables
mysql> rename table db1명.테이블명 to db2명.테이블명;          테이블을 다른 DB로 이동
mysql> alter table 테이블명 add 컬럼명 데이터타입;            컬럼추가
mysql> alter table 테이블명 del 컬럼명;                   컬럼제거
mysql> alter table 테이블명 modify 컬럼명 컬럼타입;           컬럼명에 지정된 컬럼타입의 변경
mysql> alter table 테이블명 change old컬럼명 new컬럼명 컬럼타입   컬럼명 변경
mysql> alter table 테이블명 type=innodb;                   테이블type변경
mysql> create table 테이블명(..) type=heap min_rows=10000;       10000row를 수용할 수 있을 만큼 메모리할당(heap type이므로)
mysql> select version();                             MySQL서버버전 출력
mysql> create table 테이블2 as select * from 테이블1;          테이블1과 동일한 테이블 생성(with 데이터, as는 생략가능)
mysql> create table 테이블2 as select * from 테이블1 where 1=2;   테이블1과 동일한 구조의 테이블 생성(without 데이터, 1=2는 0으로 할수도 있다.)
mysql> insert into 테이블2 select * from 테이블1;             테이블1의 데이터를 테이블2에 insert


테이블이 존재여부 파악
DROP TABLE IF EXISTS 테이블명;
CREATE TABLE 테이블명 (...);
프로그래밍 언어에서 COUNT(*)를 사용하여 질의가 성공하면 테이블이 존재함을 파악할 수 있다.
ISAM, MyISAM의 경우 COUNT(*)가 최적화되어 상관없으나, BDB, InnoDB의 경우 full scan이 발생하므로 사용하지 마라.
대신 select * from 테이블명 where 0; 을 사용하라. 질의가 성공하면 테이블이 존재하는 것이고, 아니면 존재하지 않는 것이다.

 

접속
mysql {-h 접속호스트} -u 사용자 -p 사용DB
-h로 다른 서버에 존재하는 MySQL접속시 다음과 같이 MySQL DB에 설정해줘야 한다.
mysql> INSERT INTO user VALUES('접근을 허용할 호스트ip','사용자',PASSWORD('비밀번호'),'Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');
mysql> INSERT INTO db(Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv) VALUES('접근을 허용할 호스트ip','사용DB','사용자','Y','Y','Y','Y','Y','Y');
mysql> FLUSH PRIVILEGES; or 쉴프롬프트상에서 % mysqladmin -u root -p flush-privileges


검색조건(where)
regular expression을 지원하다니 신기하군..
mysql> select * from work where 열명 regexp "정규표현식";


백업 & 복구
mysqldump {-h 호스트} -u 사용자 -p DB명 > 백업파일
mysql {-h 호스트} -u 사용자 -p DB명 < 백업파일

mysqldump -u root -p --opt db_moyiza > moyiza.sql
mysqldump -u root -p --opt db_board | mysql ---host=remote-host -C database (상이한 머쉰)
mysql -u moyiza -p db_moyiza < moyiza.sql

mysqldump -u root -p --opt db_moyiza | mysql ---host=ns.moyiza.net -C db_moyiza

테이블 생성구문만을 화면에서 보려면 다음과 같이 --no-data를 사용한다. 테이블명을 생략하면 모든 테이블 출력
mysqldump -u 유저명 -p --no-data db명 테이블명

테이블 검사
isamchk

오라클 sysdate와 동일
insert into test values('12', now());

유닉스 time()함수 리턴값 사용
FROM_UNIXTIME(954788684)
UNIX_TIMESTAMP("2001-04-04 :04:04:04")

MySQL 디폴트 DB&로그파일 위치
/var/lib/mysql
/var/lib디렉토리는 여러 프로세스들이 사용하는 데이터를 저장하는 일종의 파일시스템상의 데이터베이스라고 볼 수 있다.

replace
해당 레코드 존재하면 update하고, 존재하지 않는다면 insert한다.(insert문법과 동일)
replace into test values('maddog','kang myung gyu')'

explain
explain 질의문: 지정한 질의문이 어떻게 실행될 건지를 보여줌
mysql> explain select u.uid, u.name, a.name from sm_user u, sm_addr a where u.uid=a.uid;
+-------+------+-----------------+-----------------+---------+-------+------+-------+
| table | type | possible_keys  | key         | key_len | ref  | rows | Extra |
+-------+------+-----------------+-----------------+---------+-------+------+-------+
| u   | ALL | PRIMARY      | NULL        |   NULL | NULL | 370 |     |
| a   | ref | sm_addr_uid_idx | sm_addr_uid_idx |    11 | u.uid |  11 |     |
+-------+------+-----------------+-----------------+---------+-------+------+-------+
2 rows in set (0.01 sec)


temporary table
크기가 큰 테이블에 있는 subset에 대한 질의라면 subset을 temporary table에 저장한 후 질의하는 것이 더 빠를 경우가 있다.
temporary table는 세션내에서만 유효하고(현재 사용자만이 볼수 있다는 뜻), 세션종료시 자동적으로 drop된다.

create temporary table (...);
create temporary table (...) type=heap;     디스크가 아닌 메모리에 테이블 생성

존재하는 permanent table의 테이블명과 동일하게 생성할 수 있으며,
temporary table은 permanent table보다 우선시되어 처리된다.
4.0.7의 감마버전에서 테스트하면 결과는 약간 달라진다. 버그인건지..

mysql> create table test (id varchar(10));
Query OK, 0 rows affected (0.01 sec)

mysql> insert into test values('moyiza');
Query OK, 1 row affected (0.00 sec)

mysql> create temporary table test(id varchar(10));
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test;
Empty set (0.00 sec)

mysql> drop table test;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from test;
+----------+
| id     |
+----------+
| moyiza |
+----------+
1 row in set (0.00 sec)

 

Table Type에 다른 Files on Disk

ISAM  .frm (definition) .ISD (data) .ISM (indexes)
MyISAM .frm (definition) .MYD (data) .MYI (indexes)
MERGE .frm (definition) .MRG (list of constituent MyISAM table names)
HEAP  .frm (definition)
BDB   .frm (definition) .db (data and indexes)
InnoDB .frm (definition)

보통 mysqldump를 사용하여 백업을 수행하여 다른 DB서버에 데이터를 restore하면 된다.
MySQL은 별다른 작업없이 데이터파일을 단순히 복사(copy)하는 것만으로도 다른 서버에
DB을 이동시킬 수 있다. 하지만, 이런 방식이 지원되지 않는 table type도 있다.

ISAM: machine-dependent format하기때문에..
BDB : .db파일에 이미 테이블위치가 encode되어 있기때문에..
MyISAM, InnoDB, MERGE :가능(machine-independent format)

별다른 지정을 하지 않았다면 디폴트 TABLE type이 MyISAM이므로, 무난히 migration할 수 있다.
floating-point컬럼(FLOAT,DOUBLE)이 있다면 이러한 방식이 실패할 수 도 있다.

쉘에서는 mysql이 되는데 PHP에서 mysql.sock error를 내면서 MySQL이 안되는 경우
mysql.sock은 /tmp 아니면 /var/lib/mysql에 생기게 된다.
나의 경우, /var/lib/mysql에 mysql.sock파일이 있는데 PHP에서는 /tmp에서 찾으려하면서 에러를 발생했다.
/usr/bin/safe_mysqld파일에서 다음과 같이 수정한다.
주석(#)이 달린 것이 원래것이고 그 밑에 있는것이 수정한 것이다.

# MYSQL_UNIX_PORT=${MYSQL_UNIX_PORT:-/var/lib/mysql/mysql.sock}
MYSQL_UNIX_PORT=${MYSQL_UNIX_PORT:-/tmp/mysql.sock}

위와 같이 하니 /usr/bin/mysql이 /var/lib/mysql/mysql.sock에서 소켓파일을 찾으려 했다.
socket file을 지정하는 --socket이라는 옵션으로 다음과 같이 지정하면 된다.

mysql --socket=/tmp/mysql.sock -u moyiza -p db_test

하지만 mysql실행시마다 이렇게 써줘야한다는 것이 상당히 귀찮다. 옵션이 바로 적용되게 설정하자.
mysql은 설정사항을 다음 3가지 파일에서 검색한다.

/etc/my.cnf        global options(MySQL 전체적으로 사용되는 옵션 정의)
mysql-data-dir/my.cnf 특정 DB에 적용되는 option (/var/lib/mysql/my.cnf)
~/.my.cnf         사용자 각각의 설정('~'문자는 사용자의 홈디렉토리는 의미)

/usr/share/mysql디렉토리에 예제가 있으므로 참고한다.
소켓파일의 지정은 다음줄을 넣어주면 된다.

socket       = /tmp/mysql.sock


== /etc/my.cnf예 ==
# The following options will be passed to all MySQL clients
[client]
#password     = your_password
port        = 3306
socket       = /tmp/mysql.sock

# Here follows entries for some specific programs

# The MySQL server
[mysqld]
port        = 3306
socket       = /tmp/mysql.sock

 

MySQL에서 통계처리시
orderby, groupby 는 sort_buffer를 늘여준다.(show variables)

live table(smslog)에서 모든 질의를 처리하지 말고 summary table에 질의결과를 저장해 재질의 처리한다.
summary table이 heap-type table가 가능한지 확인할 것.

INSERT INTO tblTemp2 (fldID) SELECT tblTemp1.fldOrder_ID FROM tblTemp1 WHERE
   tblTemp1.fldOrder_ID > 100;


join이 subselect보다 빠르다.
join시 사용되는 컬럼은 동일한 column type과 길이를 가져야만 최적의 속도를 보장한다.
즉, 동일 column type이지만 길이가 다르다면(char(11), char(10)), 동일한 컬럼도메인으로 변경해주는 것이 좋다.
where의 in은 optimize되어 있으므로 빠르다
insert,select는 동시에 수행가능하다.(어떻게?)
explain으로 질의과정 점검


varchar to/from char
conversion varchar를 char로 변경할 경우 모든 컬럼타입을 동시에 변경해야 한다.
반대의 경우, 하나만 char->charchar변경시 다른 모든 컬럼도 varchar로 변경됨
참.. 특이하구만..

mysql> CREATE TABLE chartbl (name VARCHAR(40), address VARCHAR(80));
Query OK, 0 rows affected (0.05 sec)

mysql> desc chartbl;
+---------+-------------+------+-----+---------+-------+
| Field  | Type     | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| name   | varchar(40) | YES |   | NULL   |     |
| address | varchar(80) | YES |   | NULL   |     |
+---------+-------------+------+-----+---------+-------+
2 rows in set (0.03 sec)

mysql> alter table chartbl modify name char(40);
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> desc chartbl;
+---------+-------------+------+-----+---------+-------+
| Field  | Type     | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| name   | varchar(40) | YES |   | NULL   |     |
| address | varchar(80) | YES |   | NULL   |     |
+---------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

mysql> alter table chartbl modify name char(40), modify address char(80);
Query OK, 0 rows affected (0.01 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> desc chartbl;
+---------+----------+------+-----+---------+-------+
| Field  | Type   | Null | Key | Default | Extra |
+---------+----------+------+-----+---------+-------+
| name   | char(40) | YES |   | NULL   |     |
| address | char(80) | YES |   | NULL   |     |
+---------+----------+------+-----+---------+-------+
2 rows in set (0.00 sec)

mysql>


"For each article, find the dealer(s) with the most expensive price."

표준안
   SELECT article, dealer, price
   FROM  shop s1
   WHERE price=(SELECT MAX(s2.price)
             FROM shop s2
             WHERE s1.article = s2.article);

수정안(최적화)
   CREATE TEMPORARY TABLE tmp (
         article INT(4) UNSIGNED ZEROFILL DEFAULT '0000' NOT NULL,
         price  DOUBLE(16,2)         DEFAULT '0.00' NOT NULL);

   LOCK TABLES shop read;

   INSERT INTO tmp SELECT article, MAX(price) FROM shop GROUP BY article;

   SELECT shop.article, dealer, shop.price FROM shop, tmp
   WHERE shop.article=tmp.article AND shop.price=tmp.price;

   UNLOCK TABLES;

   DROP TABLE tmp;

 

========================================================
MySQL 특성정리
========================================================
primary key, foreign key지원
index 지원(15개컬럼, 256byte까지)
MySQL에서의 Stored Script개념 => SQL server language
commit-rollback개념 => lock tables(lock table test write -> 트랜잭션.. -> unlock tables)
컬럼명길이: 64자까지, 컬럼 Alias: 256자까지
not case-sensitive: keywords, functions, column, index명
case-sensitive: database, table, alias명
키워드,함수명은 대소문자구별이 없지만, db명과 table명은 Unix계열이라면 case-sensitive하다.
(이는 오브젝트명이 OS의 fs에 따라 저장되기 때문이다. 서버의 lower_case_table_names 변수를
1로 설정하면 오브젝트명은 모두 소문자로 저장되므로 유닉스-윈도간 호환성을 높일 수 있다.

지원되지 않는 부분:
Stored Procedure(5.0이상부터 지원된다고 함)
View(5.0이상부터 지원된다고 함)
Trigger(5.0이상부터 지원된다고 함)
subquery(4.1이상부터 지원된다고 함)
union, union all(4.0이상부터 지원됨)

[테이블 type에 따른 인덱스 특성]
Index Characteristic        ISAM   MyISAM         HEAP     BDB           InnoDB
NULL values allowed         No    Yes           As of 4.0.2 Yes           Yes
Columns per index          16    16            16       16            16
Indexes per table          16    32            32       31            32
Maximum index row size (bytes) 256   500           500      500/1024        500/1024
Index column prefixes allowed  Yes   Yes           Yes      Yes           No
BLOB/TEXT indexes allowed     No    Yes(255 bytes max) No       Yes (255 bytes max) No


인덱스 생성
- alter table을 이용한 인덱스 생성이 더 flexible함
- 인덱스명은 생략가능

ALTER TABLE 테이블명 ADD INDEX 인덱스명 (인덱스컬럼);
ALTER TABLE 테이블명 ADD UNIQUE 인덱스명 (인덱스컬럼);
ALTER TABLE 테이블명 ADD PRIMARY KEY (인덱스컬럼);
ALTER TABLE 테이블명 ADD FULLTEXT (인덱스컬럼);

CREATE INDEX 인덱스명 ON 테이블명 (인덱스컬럼);
CREATE UNIQUE INDEX 인덱스명 ON 테이블명 (인덱스컬럼);
CREATE FULLTEXT INDEX 인덱스명 ON 테이블명 (인덱스컬럼);

unique인덱스와 primary key인덱스와의 차이
unique은 null허용하지만, primary key는 null허용 안함
unique은 하나의 테이블에 여러개 올 수 있지만, primary key는 하나만 존재

테이블생성시 지정
CREATE TABLE 테이블명
(
... column declarations ...
INDEX 인덱스명 (인덱스컬럼),
UNIQUE 인덱스명 (인덱스컬럼),
PRIMARY KEY (인덱스컬럼),
FULLTEXT 인덱스명 (인덱스컬럼),
...

);


index prefix 생성
- 컬럼의 전체길이중 일부만 인덱스로 사용
- supported for ISAM, MyISAM, HEAP, and BDB tables, but not for InnoDB tables
- 지정되는 길이는 byte단위가 아닌 charater단위이므로, multi-byte character일 경우 주의
- blob, text 컬럼타입일 경우, index prefix 가 유용(255 길이까지 가능)

CREATE TABLE 테이블명
(
name CHAR(30) NOT NULL,
address CHAR(60) NOT NULL,
INDEX (name(10),address(10))
);


인덱스 삭제
DROP INDEX 인덱스명 ON 테이블명;
ALTER TABLE 테이블명 DROP INDEX 인덱스명;
ALTER TABLE 테이블명 DROP PRIMARY KEY;


outer join

[MySQL]
left outer joing : SELECT t1.*, t2.* FROM t1 LEFT OUTER JOIN t2 ON t1.i1 = t2.i2;
right outer joing: SELECT t1.*, t2.* FROM t1 RIGHT OUTER JOIN t2 ON t1.i1 = t2.i2;

[Oracle]
left outer joing : SELECT t1.*, t2.* FROM t1, t2 where t1.i1 = t2.i2(+);
right outer joing: SELECT t1.*, t2.* FROM t1, t2 where t1.i1(+) = t2.i2;

SELECT
student.name, student.student_id,
event.date, event.event_id, event.type
FROM
student, event
LEFT JOIN score ON student.student_id = score.student_id
       AND event.event_id = score.event_id
WHERE
score.score IS NULL
ORDER BY
student.student_id, event.event_id;


:= 문장을 이용한 변수의 설정

현재 moyiza의 데이터베이스강좌게시판에 등록된 총 게시물은 43개이다. 43개의 강좌를 읽은 수(hit수)는 각각 다르다.
평균 hit수를 구해 보자.

mysql> select @total_hit := sum(hit), @total_record := count(*) from zetyx_board_database;
+------------------------+---------------------------+
| @total_hit := sum(hit) | @total_record := count(*) |
+------------------------+---------------------------+
|             3705 |                43 |
+------------------------+---------------------------+
1 row in set (0.00 sec)

mysql> select @total_hit/@total_record as 평균HIT;
+-----------------+
| 평균HIT      |
+-----------------+
| 86.162790697674 |
+-----------------+
1 row in set (0.00 sec)

 

select substring(subject from 9) from zetyx_board_database where substring(subject, 1, 8) = '[ORACLE]';


보통 상용DBMS들이 row-level locking을 지원한다. 쉽게 말해 레코드단위로 락킹한다는 말이다.
반면, MySQL의 MyISAM 테이블타입은 table-level locking을 사용한다.
쉽게 말하면, insert, update, delete작업은 전체 테이블에 락을 걸고 처리된다는 것이다.
row-level락보다 비효율적이지만,.. MySQL은 빠르기 때문에 이 단점이 상쇄된다.

Compressed MyISAM(packed MyISAM)
정적인 테이블데이터는 압축하여 20-60%정도의 공간을 절약할 수 있다.
Production데이터를 CD로 받아서 차후 디스크에 풀지 않고 CD자체로 바로 사용할 수도 있다.
gzip등으로 백업받으면 이를 푸는 과정이 필요할 것이다.
% myisampack moyiza.myi

데이터베이스 게시판의 Merge Table에 좀 더 자세한 내용을 적어 두었다.


RAID Table
1개의 테이블은 OS상에 3개의 파일로 구성된다.
스키마파일(.frm), data파일(.myd), index파일(.myi)
MySQL의 RAID테이블은 데이터파일(.myd)을 여러개의 파일들로 구성하는 것이다.

create table raid_test (...)
type=myisam raid_type=striped raid_chunks=4 raid_chunsize=8

테이블을 4개의 데이터파일로 나누고, 8kb단위로(8kb stripe) 라운드로빈 방식으로 write가 이루어진다.

This article comes from moyiza.net (Leave this line as is)

Posted by '김용환'
,

http://www.inetmedia.co.uk/sony/Sonylaptop.html#modules

 

Sony PCG-FX501

Linux Install Guide

This is written specifically for redhat 7.2. You will need to recompile the kernel(don't worry, instructions on howto will follow). I have indicated the problems and solutions as I found them.

First the steps involved

0. Repartition Hard disk on Laptop

1. Install Redhat

2. Add modules to redhat install to allow recompile of kernel

3. Get the new Kernel

4. Reboot and copy new kernel source/kernel config to laptop

5. Compile kernel using config and source

6. Modify Lilo.conf

7. Give it a try

8. Modify the network card setup

Use partition Magic or similar to repartition your laptop, to allow dual boot of winXP and Linux. For this example, the 20Gb hard disk was split into a 6Gb partition for XP and a 14Gb partition for Redhat Linux. The choice is yours.

Back to Index

Install redhat 7.2 from the cdrom. Select everything you desire exactly as a standard install. note. Things will go more smoothly if you select the developers workstation, however the following assumes that you have chosen the standard laptop setup.

note: you will notice that when you reboot, you will see a kernel panic with output similar to below

Back to Index

First you need to ensure you have 5 additional "modules" installed(note if you did a developer workstation install you can skip this step)

Insert the redhat cd1 cdrom and reboot, this should result in the "boot:" prompt.

Next select Enter, or just wait 20 seconds or so and it will automatically start the setup program.

Next select your language(English)

Next select Keyboard Model, Layout and deadkeys as desired(or leave the defaults)

Next select your mouse

You should now be presented with a choice of installation type. In this case you should choose upgrade and select Next

Next you should be presented with a check box, saying "Customize packages to be upgraded". Select(or tick) this box

. Next select the boot laoder - I recommend Lilo here as it is well documented and easier for Nubbies...

On the same screen on the INstall Boot Loader record on:- select /dev/hda Master Boot Record (MBR)

Note: On some newsgroups for various other sony laptops I have seen a recommendation to use /dev/hda1 FIrst sector of boot partition. However, the MBR has been seen to work on my laptop.

Also, note that the /hda and /hda1 may be different on your laptop, depending on your configuration.

Accept the defaults in the remaining Partitin section(unless you know what you are doing) and select Next to go to the next page

Next you should select the following 5 components under the Tree View:-

Development\Languages\gcc

Development\Languages\gcc-C++

Development\System\kernel-headers

Development\System\kernel-source - note this is not required, but helps to have it on your system just in case

Develpement\Tools\patch



Select Next and continue

The "About to upgrade" screen appears, select Next if you are happy to proceed.

Various "preparing to install bars should appear, sit back enjoy/

At the prompt, insert cd2 from the redhat collection.

You will receive a "No kernel packages were installed...." message box, select OK.

Next you have the option to create a boot disk, select next

note you can skip this step, its your call.

Finally you reach the Congratulation screen, remove the boot floppy, the redhat cd2 cdrom and select EXIT to reboot

Back to Index

Next You need to obtain a download of the Kernel. At this time, the only kernel images that I have tested are 2.4.16 and 2.4.17. I have not tested any other version at this time. The kernel image is available from the following link ftp://www.kernel.org/pub/linux/kernel/v2.4/. The file to download is linux-2.4.16.tar.gz. You can use later versions of the kernel, but this version is known good and has been running on my laptop for 2 months now.

Back to Index

In order to get the downloaded file onto the laptop, there are a mix of methods, the one used here is to copy the kernel image onto the windows partition(any other suggestions here greatly appreciated) and then use the redhat cd to boot to a working console prompt. The instructions follow:-

Start windows, then copy the kernel source to "c:\temp".

At this point you reboot using cd1 from the redhat cd collection

This should result in the following ommand prompt:

boot:

At this command prompt type:

linux rescue

And press return

Next select your language and press OK

Next select your keyboard and press OK

You should see a "Running anaconda - please wait"

You will now see a "Rescue" dialogue box that indicates that it will try to find your redhat image and mount it under the directory /mnt/sysimage. Select Continue and press return

This should have found your redhat image, if not, time to visit some newsgroups, however if it does, note the following in the Rescue dialogue box "chroot /mnt/sysimage" - you will need this command to generally make your life easier.

Select OK

12. Congratulations, you now have a shell(ie a command prompt for DOS people)

13. You can now run the following command

chroot /mnt/sysimage

Note: All this command is doing is basically setting the root of the tree to a directory two steps down from the root. This is useful if you are doing any compiling, as you will be doing in just a few minutes.

Next create a directory to temporarily store the kernel source. For this I created a directory as follows:-

cd /usr

mkdir kernelsrc

cd /kernelsrc

Next copy the kernel image from the windows partition using the following commands:

mkdir /cdrive

mount -t vfat /dev/hda1 /cdrive

cd /cdrive/temp

cp * /kernelsrc

Next uncompress the file as follows:-

gunzip linux-2.4.16.tar.gz

Which should uncompress the file. List the file name in the directory to verify as follows:-

ls -al

which produces the follwing output. Notice the .gz extension has gone

-rw--r--r- 1 scott scott 129699840 Nov 27 10:56 linux-2.4.16.tar

Next untar the file as follows: this will create a directory strucuture below the existing directory

tar -xvf linux-2.4.16.tar

If you now do an "ls -al" you will notice the original file, plus a directory call "linux"

Next type:-

cd linux

ls -al

Back to Index

So far so good, you are now ready to start the steps towards your new kernel. Here are the commands, but don't do all of them yet, I will take you through each of them.

make menuconfig

make mrproper

make dep

make bzImage

make modules

make modules_install

cp /usr/kernelsrc/linux/arch/i386/boot/bzImage /boot/vmlinuz-2.4.16

lilo

During menuconfig, you are prompted to include certain items directly into the kernel. You must find and choose all of the following:- Note, I have a copy of my config, which you can copy into the /usr/kernelsrc/linux directory instead of doing this step. Then type "make menuconfig" then select "Load an Alternate Configuration File", and choose the file "sonyPCG-FX501". Then save the new configuration and exit. If you do this, then miss the following steps.

....work to be done here, incomplete..... should use file above for now.......

Next you can now start the compile process

make dep

make bzImage

make modules

make modules_install

You can verify the modules have been correctly copied, using the following:

cd /lib/modules

ls -al

You should see two directories, 2.4.7-10 and the newly created 2.4.16, congrats.

Next you must copy the new boot image to the correct location. Don't skip this step!

cp /usr/kernelsrc/linux/arch/i386/boot/bzImage /boot/vmlinuz-2.4.16

This sets puts the boot image exactly where it is needed.

Next create a ramdisk image of the kernel

mkinitrd /boot/vmlinuz-2.4.16.img 2.4.16

Next, edit the /etc/lilo.conf file and add the following at the end of the file:-

image=/boot/vmlinuz-2.4.16
label=linux-2.4.16
initrd=/boot/initrd-2.4.16
append= " nobiospnp mem=nopentium"
read-only
root=/dev/hda3


Note, hda3 may be different on your laptop.

Back to Index

Finally, run the following commands:

cd /etc

lilo

This command "lilo" is essential and is often forgotten. It basically tells the OS the exact physical location on the hard disk where the new kernel image lies.

Back to Index

That should be it. Now reboot and choose the linux-2.4.16 option on the lilo screen and you should have a working version of redhat on your Sony......

Back to Index

Once you have logged in, edit the file /etc/modules.conf and add the following line below the "alias eth0 8130too" line

options 8139too io=0x1800 irq=10

For simplicity, reboot and this should enable the network. Note you may need to check that the above settings are correct on your laptop, to do this, simply use Preferences,Information, PCI and search through for "Ethernet Controller: Realteck Semiconductor Co." - below this line will be your io and irq settings, though I doubt they will be different from above.

Any emails on your success appreciated.

Back to Index - Nothing more here

.

 

Posted by '김용환'
,

 

 

 

http://phpschool.com/gnuboard4/bbs/board.php?bo_table=tipntech&wr_id=16204&sca=%BC%B3%C4%A1%2F%BC%B3%C1%A4&page=11

 

 

안녕하세요~
얼마전 회사에서 삼바 서버를 설치할 필요가 있었습니다.
여러 곳의 문서를 참고했지만..틀린 부분도 많고 해서 설치에 어려움이 많았습니다.

삼바 설치 하는 법을 간략하게 정리해 보았습니다.

많은 도움 되셨으면 합니다. 틀린 부분 있으면 많은 지적 부탁드립니다.

참고로 RedHat Linux 7.2 에서 설치 해 보았습니다.

***********************************
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Samba 서버 설치
***********************************

1. 삼바 소스(samba2.2.7a.tar.gz)를 다운로드 합니다.
http://kr.samba.org/samba/download.html

2. 적당한 곳에 소스파일을 옮깁니다. (예: /usr/local/src)

3. 압축을 풉니다.
shell>tar zxvf samba2.2.7a.tar.gz

4. 압축 푼 삼바 폴더 내 source 디렉토리로 이동 한 후.
./configure (기본적으로 /usr/local/samba/ 라는 폴더가 prefix 로 잡혀 있습니다.)
make
make install

5. 삼바 환경설정 파일인 smb.conf 는 소스로 설치시 자동 복사가 되지 않기에,
삼바소스(여기서는 /usr/local/src/samba2.2.7a)의 examples 디렉토리에 있는 smb.conf.default 파일을
/usr/local/samba/lib/smb.conf 파일로 복사해 주세요
(기본 설치시 smb.conf path가 /usr/local/samba/lib/ 입니다.)

6. 삼바 소스 디렉토리 내의 packaging/RedHat 하위 디렉토리에 있는 삼바 구동 스크립트 파일인 smb.init를 /usr/sbin/samba 파일과 /etc/rc.d/init.d/samba 파일로 복사합니다.
삼바구동에 필요한 데몬파일 smbd,nmbd 파일을 /usr/sbin 디렉토리로 복사합니다.

7. /etc/rc.d/init.d/samba 파일을 vi 로 열어서 편집합니다.
CONFIG=/usr/local/samba/lib/smb.conf 로 변경하고 저장, vi 종료합니다.

8. 복잡한 smb.conf 파일을 웹상에서 핸들링 가능한 swat 을 이용하기 위해(SWAT 설정)
/etc/xinetd.d/swat 이라는 파일을 생성하고..아래와 같은 내용을 집어 넣습니다.

## /etc/xinetd.d/swat
service swat
{
disable = no
port = 901
socket_type = stream
wait = no
user = root
server = /usr/local/samba/bin/swat
log_on_failure += USERID
}

위의 server 항목은 swat 실행 파일이 위치한 경로를 지정해 주면 됩니다.
삼바 설치된 디렉토리에서 찾으니 저는 /usr/local/samba/bin 아래에 있더군요.

9. xinetd 데몬 재기동
shell>/etc/init.d/xinted restart
OK라는 표시가 나와야 합니다. swat은 samba 데몬과는 상관없다고 합니다.

samba 데몬을 재기동 시키기 위해선..
/etc/init.d/sambar restart 하시면 됩니다.
ps -ef | grep smbd
ps -ef | grep nmbd 하시면 smbd -D 이런 식으로 나오면 정상 기동 된 것입니다.

데몬이 기동되지 않는 것은 주로 경로 설정 부분이니..위의 내용을 잘 읽어보고 하시기 바랍니다.

나머지는 웹브라우저에서 http://해당 IP:901/ 하신 후 , 아이디에 root, 서버 root 비밀번호 입력하신 후 설정하시면 됩니다.


swat 설정에 관한 부분은 여러가지 문서가 많이 있더군요..검색해서 보시면 금방 아실 수 있을듯 합니다.

samba 서버 설치, 구동 부분의 문서가 체계화 되어 있지 않는것 같아 정리해 보았습니다.

 

Posted by '김용환'
,

ifdown ifup ifconfig

c or linux 2006. 11. 17. 23:14

 

http://kin.naver.com/db/detail.php?d1id=1&dir_id=10202&eid=/D5lRaU0UWxKALWM3h7jdPvXfNsf46RT&qb=ZXRoMCBpZmNvbmZpZyBkaGNw

 

 

 

윈도우에서 콘솔화면에서 현재 랜카드를 DHCP로 사용하고 있는 중에는..
ipconfig /renew, ipconfig /release 이렇게 하면..
아이피 주소를 새로 할당 받고, 아이피를 해제 하는데..
프로그래밍에서는 ipReleaseAddress, ipRenewAddress 함수가 존재하는데..
리눅스에서는 윈도우에서 처럼 지원하는게 있나요?
service network restart 이런거 말고요..
리눅스 프로그래밍에서 사용할 수 있는 함수나..
리눅스 터미널에서 사용 가능한 방법 좀 알려주세요..

 

service (/etc/init.d/network) network restart 명령어는 말 그대로 network 데몬을 재시작하는 명령어입니다.

DHCP로 할당이 되어 있다면 시작을 하면서 IP주소를 DHCP서버로부터 받아 오지요

 

이 방법이 아니면 (만약 eth0 이 꽂아져 있는 랜카드라면)

# ifdown eth0

# ifup eth0

명령어를 수행하시면 다시 받아 오는 역할을 하구요.

 

파일은

/etc/sysconfig/network-scripts/ifcfg-eth0 (1,2,3,4,) 을 vi 로 여시고 수정하시고 위에 ifdown, ifup 명령어를 수행하시면 됩니다.

 

network 상태를 보는 명령어는

# ifconfig 이구요 (ipconfig 와 비슷하죠?)

# ifconfig -a 하시면 전체 인터페이스에 대한 결과를 보여 줍니다.

 

장비에 랜선이 제대로 연결되어 있는 지를 보는 명령어는

# mii-tools 라고 치시면 됩니다.

 

 

Posted by '김용환'
,
KLDP에서.


좋은 프로그래머란 무엇인가에 대한 다양한 논점을 풀이한 노트이다.

초보자에게 맞는 다음과 같은 이야기를 알려준다.

디버그에 대한 얘기를 서두로 하여, 로그 및 성능, I/O, 메모리 관리, 설계 능력, 실행 하는 법을 얘기하고 있다.
시간 추정하는 법(일정 스케쥴), 정보를 찾는 법, 문서화하는 법과 같이 쉽게 지나칠 수 있는 부분을 다시 한 번 꼼꼼하게 챙기고 있다. 특히 재미있는 것은 막힐 때는 쉬라고 하고, 집에 갈 시간을 인지하는 방법도 나온다는 것이다. 특히, 가를 죽이고 싶은 충동이 있으면 집에 가라는 문구는 동감하는 바이다.
수동적인 성향이 강한 초보자에게 제대로 설명이 되어 있다.

중급자에게 알려주는 이야기는 다음과 같다.
역시 의욕문제, 신뢰 받는 법, 균영 잡는 법, 압박 검사를 하는 법, 간결성/추상성의 균형을 잡는 법, 신기능을 배우는 법, 통합검사를 하는 법, 의사소통을 하는 법등을 알려주고 있다.
또한 개발 시간을 관리하는 법, 타사의 위험부담을 아는 법, 컨설턴트를 아는 법, 적당하게 회의하는 법, 의존성을 관리하는 법, 완성도를 판단하는 법, 구입과 개발 사이를 결정하는 법, 전문가로 성장하는 법, 면접 대상자를 평가하는 법, 화려한 전산교육을 적용할 때를 아는 법등을 제시하고 있다.

이중, 재미있는 것은 요구사항과 관련된 부분이다. 비기술자들과 같이 일할 때, 기술자가 무질서에서 질서를, 혼동됨에서 명확함을 찾아내도록 훈련받은 부분에서 빠질 수 있는 더 좋은 종합적 해결책을 찾는 데 큰 도움을 받을 수 있다는 점이다. 아이디어와 그에 대한 비용 추정을 신속하게 짝지어서 비용과 이득이 최상으로 결합되는 아이디어를 찾을 수 있게 해 줄 수 있다는 것이다.

상급자에게 알려주는 글은 다음과 같다.
성공에 때한 뚜렷한 정의가 없다면 성공할 수 없다라는 것은 명제에 두고 있다.
내장 언어를 활용하는 방법을 제시하고 있다. 새로운 것을 만들기 전에 항상 기존에 쓰이던 언어가 있는 지 판단하고, 잘 사용하는 법이 좋을 수 있다는 것이다.
언어를 잘 선택해야 한다. 언어에 대한 독재는 그것이 주는 유익보다 거기에 드는 비용이 더 크다라는 사실이다.
사용자를 이해하는 법을 알아야 한다. 사용자들의 말을 듣는 것에 더하여 그들이 무엇을 하는지 관찰하는 것의 중요성을 강조한다.
진급하는 법을 알려주고 있다. 높은 직위로 진급하고 싶다면, 그에 걸맞는 그 직위에 기대되는 것이 무엇인지 파악하여 그것을 행해야 한다는 것이다. 스스로 무엇이 필요하다고 인식하는 것과 상사가 필요한 것이 다를 수 있기 때문에 명확히 하는 것이 중요하고, 그에 걸맞은 업무지식을 쌓아야 한다.

재능을 개발해야 한다. 이는 리더쉽을 의미한다. 자신뿐 아니라, 자신을 거느리고 있는 팀원에게 동기 부여와 긴장유지를 위해서 힘써야 하고 칭찬은 공개적으로, 비판은 사적으로 해야 한다라는 간단한 원리를 머리속에 집어넣줘야 한다.
능력있는 팀원은 좀 더 어려운 과제를 줄 수 있어야 하는 부분을 통해서 팀원의 모두를 윈윈할 수 있는 전략을 넣어야 한다. 최대한 능력을 발휘할 수 있도록해야 한다. 자주 칭찬하되 분별없이는 하지 말구, 의견이 달라도 칭찬할 수 있는 용기 있는 사람이 되도록 해야 한다.
문제를 잘 나눌 수 있어야 한다. 따분한 과제를 다루는 법, 프로젝트 지원을 얻는 방법, 시스템이 자라게 하는 방법을 잊지 않도록 해야 한다.


이 글 전체적으로 상당히 경험에서 우러 나오는 말을 하고 있다. 이 하나하나를 마음 속에 두고, 제목을 볼 때마다 항상 내 자신에게 채찍질하며 열심히 프로그래머로서 살 수 있도록 노력해야 겠다..

Posted by '김용환'
,
http://bbs.kldp.org/viewtopic.php?t=69993&postdays=0&postorder=asc&highlight=void+%2Athis&start=40

요즘 KLDP에서 C를 C++처럼 사용하는 법에 대해서 토론중이다.
(OOP, 프레임웍, 패턴이라는 단어들이 사용하면서 긴장을 풀고 살았다라는 생각이 드는 것은 무엇일런지..
초심의 마음으로 다시 접근해야 겠다.)



#include <stdio.h>

struct _my_str;

void inc_one(struct _my_str *);
void dec_one(struct _my_str *);

typedef void (*void_func) (struct _my_str *);

typedef struct _my_str {
   int iLen;
   void_func inc;
   void_func dec;
} my_str;

my_str* init_my_str() {
   my_str *new_my_str = (my_str*) malloc (sizeof(my_str));
   new_my_str->iLen = 0;
   new_my_str->inc = &inc_one;
   new_my_str->dec = &dec_one;
   return new_my_str;
}

void inc_one(my_str *my) {
   my->iLen++;
}

void dec_one(my_str *my) {
   my->iLen--;
}

main() {
   my_str *a = init_my_str();
   printf("a->iLen = %d\n", a->iLen);
   a->inc(a);
   printf("a->iLen = %d\n", a->iLen);
}

함수포인터를 이용하여 구현한 것으로서, 자신의 객체를 내부에서 구현하고 참조하는 C++, java의 실제 내부 구현이기도 하다.
전역변수를 쓰지 않음으로서 메모리를 아끼고, 마치 객체처럼 편리하게 쓰일 수 있다. OOP의 encapsulation처럼 쓰일 수 있는 장점이 있다. 물론 structure의 변수들을 모두 접근한다는 부분이 걸리지만 말이다.
 
Herb Sutter 님의 Exceptional C++ 에서는 다음과 같이 c를 이용하여 OOP의 편리함을 이용하고 있다.

List of "foo.c" :
코드:
#include "bar.h"

int main()
{
 BAR *bar = create_bar();
 use_bar(bar);
 remove_bar(bar);
 return 0;
}


List of "bar.c" :
코드:
#include "bar.h"

struct BAR_
{
int somedata1;
int somedata2;
void *memory;
};
typedef struct BAR_ BAR;

BAR *create_bar() { static BAR b; return &b; }
void use_bar(BAR *) {  }
void remove_bar(BAR *) { }


List of "bar.h" :
코드:
struct BAR_;

struct BAR_ *create_bar();
void use_bar(struct BAR_ *);
void remove_bar(struct BAR_ *);
Posted by '김용환'
,

uCOS-ii V2.51을 8051에 포팅하는 포팅기를 적어볼까 합니다.



기존에 포팅경험이 있으신 분들에게는 별 시덥지 않은 포팅기가 될지도 모르지만,

처음 접하시는 분들은 많은 시행착오를 겪어야 하기 때문에,

조금이나마 그 시행착오를 줄이는데 도움이 됐으면 합니다.



uCOS를 포팅하는 방법은 Jean Labrosse님께서 쓰신 uCOS-ii 책에 자세히 설명되어 있으며,

인터넷상에 돌아다니는 문서도 많기 때문에,

포팅의 공통된 부분은 기존 책과 인터넷 자료를 참고하시기 바라며,

저는 8051에 포팅시 주의할 점 위주로 설명할까 합니다.



/********* AT89S52 에 uCOS-ii V2.51 포팅 준비하기. **********/



AT89S52는 ATmel (www.atmel.com) 에서 나온 8051제품입니다.

포팅의 가장 기본은 포팅하고자 하는 MCU에 대해서 이해하는 일이라고 생각하기에

AT89S52의 Features에서 uCOS-ii를 포팅할때 이해해야 할 부분에 대해서만 간단하게 설명하고 넘어가도록 하겠습니다.



AT89S52는 내부에 8Kbyte의 플래쉬 메모리와 256Byte의 내부램을 가지고 있습니다.

내부 플래쉬 메모리는 ISP기능을 지원하므로 사용자가 쉽게 프로그래밍할수 있게 되어 있습니다.



AT89S52는 최대 24MHz 와 33MHz 까지 지원하는 제품으로 나뉘어 있습니다.

24Mhz까지 지원하는 제품은 제품명뒤에 제품코드로 AT89S52 - 24AC 이런식으로 24MHz 지원 제품이라는 코드가 붙습니다.

33Mhz도 마찬가지로 AT89S52 - 33AC 라는 코드가 붙습니다.

이외에도 다른 코드들이 몇가지 더 있으며, ATmel에서 제공하는 AT89S52 데이타 쉬트를 참고하시기 바랍니다.

( 데이타쉬트는 www.atmel.com 에 있습니다.)



내부 플래쉬 메모리와 내부램 , 클럭 이정도만 알면, MCU에서 포팅에 필요한 기초적인 부분은 알았다고 생각되는데요....^^;;

uCOS 의 코드가 들어갈 플래쉬 메모리, uCOS에서 사용할 램, 그리고 uCOS에서 필요한 Tick을 제공할 클럭.

정말 간단하지 않습니까?? ^^



그럼 AT89S52 만으로 uCOS를 포팅할수 있을까?? 하는 부분에 대해서 생각해 봐야겠죠??



참고로 제가 포팅해본 바로는 uCOS가 컴파일 되었을때, 대략 7Kbyte의 코드 사이즈와 1.2kByte 정도의 내부램을 사용합니다.

물론, 옵티마이즈했을 경우에는 달라지겠지만 말입니다.

코드사이즈야 엇비슷하게 나오니...태스크 코드의 양이 많지만 않다면, AT89S52의 내부 플래쉬 메모리를 사용해도 되겠지만...

램의 경우에는 턱없이 부족하게 되죠...^^



그러므로 좀더 넉넉한 코드 메모리와 데이터 메모리를 위해서 외부에 롬과 램을 확장해서 사용해야 하겠습니다.

외부에 롬과 램을 확장하기에 어려운 환경이라면,

ATmel 에서 나온 AT89C51ED2 같은 경우에는 64Kbyte의 플래쉬 메모리와 2K바이트의 내부 램을 지원하므로 이런

디바이스들을 사용하시면 되겠죠??



이제 uCOS를 어떤 컴파일러를 사용해서 컴파일 하느냐가 문제네요...

uCOS는 ANSI C 된 부분과, 약간의 어셈블리어 소스로 구성되어 있습니다.

ANSI C로 된 부분은 8051용 C컴파일러라면 모두 지원하기에 상관이 없지만,

어셈블리어 부분에서는 컴파일러마다 조금씩 다르므로 이부분도 염두에 두셔야 합니다...



8051컴파일러는 여러 종류가 있고... 그중에서 가장 많이 쓰이는 컴파일러는 Keil이라고 생각되는데요....

개인적으로는 Keil이 많은 사람이 쓰기에 인터넷 상에 자료도 많고,

또 www.keil.com 에서 제공해주는 소스 샘플이나,

Q&A도 상당히 풍부하기에 이용하기에는 가장 좋다고 생각합니다.

또 써본바로는 디버깅환경이 실제 환경과 상당히 정확하고, 코드 옵티마이즈 부분또한

타 컴파일러에 비해서 상당히 좋다고 느꼈습니다.



이제 컴파일러와 MCU에 대한 부분이 다 준비가 되었으므로...

uCOS-ii 포팅 소스를 구해오면 포팅 준비는 끝났다고 봅니다...



포팅 소스는 www.uCOS-ii.com 사에트에서 제공되는 8051용 포팅소스를 받으시면 됩니다.

프리 포트 다운로드에 가셔서 Intel 란으로 들어가시면 keil 용으로 포팅된 포팅소스가 있습니다.

그중에서 uCOS-ii 버젼 V2.52 용으로 된것을 이용하도록 하겠습니다.

제가 가지고 있던건 V2.51 인데... 별차이가 없기에..^^ 그냥 쓰셔도 됩니다.

만약에 다른 버젼의 uCOS-ii 소스를 가지고 계시다면 다른걸 받으셔도 무방합니다.

( 다운받으신 포팅소스에는 포팅화일만 있습니다. uCOS-ii 소스 화일은 Jean Labrosse님께서 쓰신 uCOS-ii 책에서

제공되는 CD에 있는 소스를 사용하시거나, 알아서 구하셔야 합니다...^^)



그럼 이제 포팅에 필요한 준비는 끝이 났습니다.


MPU : AT89S52 (ATEML社)

uCOS-ii 버젼: V2.51

컴파일러 : Keil v7.20



위와 같은 환경에 저번 포팅기에서 이야기했던,

uCOS-ii 포팅소스와 커널 소스를 가지고 포팅을 진행해 보도록하겠습니다.



각자 취향이라고 해야할까요??

취향마다 포팅시에 손대는 부분이 다를테니깐요..

저는 제방식대로 포팅을 진행하도록 하겠습니다.



이미 포팅이 되어있는 소스를 가지고 시작하는 것이기에...

어려운 일은 아니니 천천히 따라해보면 쉽습니다.



그럼 이제 AT89S52의 외부 프로그램 메모리와 외부 데이터 메모리에 포팅을 시작해 보도록 하겟습니다.



우선, 준비된 화일들을 다시한번 확인해 보도록 하겠습니다.



uCOS-ii 포팅소스:

<a href="http://www.ucos-ii.com/contents/products/ucos-ii/downloads/keil%20uCOS_II%20v2.52%20port.zip">http://www.ucos-ii.com/contents/products/ucos-ii/downloads/keil%20uCOS_II%20v2.52%20port.zip</a>



위 화일을 받으신후 압축을 푸시면 여러 화일들이 나옵니다.

그중에서 아래의 화일들만 분리해서 이제 포팅을 진행할 폴더에 복사하시기 바랍니다.

예) c:\keil\engine\ucos\port



INCLUDES.H -> 마스터 인클루드 화일 헤더화일들의 대표입죠..^^

OS_CFG.H -> OS 환경 설정 헤더 화일

OS_CPU.H -> OS CPU의존적인 설정 환경화일

OS_CPU_A.A51 -> OS CPU 의존적인 어셈화일

OS_CPU_C.C -> OS CPU 의존적인 C화일

os_kc51.h -> OS Keil 의존적인 헤더화일

os_kcdef.h -> OS Keil 의존적인 define 설정화일

EX1L.C -> main 함수가 들어있는 화일

uCOS_II.H -> uCOS_ii 정의 화일



받으신 포팅소스는 Keil에서 지원하는 PC에뮬레이터 소스가 같이 첨부되어 있는데...

불필요한 소스이기에 쓰지 않도록 하겠습니다.



uCOS-ii 커널소스:

커널 소스는 커널소스 폴더에 넣으시기 바랍니다.

예)c:\keil\engine\ucos\source



작업폴더는 c:\keil\engine\ucos 이곳이 되며, 이곳에 Keil 프로젝트 화일을 생성하시기 바랍니다.





/*************************************** 작업의 시작 1 ******************************************/



주의1: 포팅시에 에러가 난다고 해서, source 화일에 있는 ucos-ii 커널 소스는 절대 건드리시면 안됩니다. ^^



이제 포팅 소스들을 살펴보도록 하겠습니다.

아래에 보이는 소스는 ucos-ii.com 에서 받으신 소스를 AT89S52에 맞게 수정한 것입니다. 그러니 소스를 살펴보시면서

수정된 부분을 똑같이 수정하시기 바랍니다.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

< INCLUDES.H >



#ifndef __INCLUDES__
#define __INCLUDES__

#include <REGX51.H> //<-- AT89S52를 위한 헤더화일로 수정

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <setjmp.h>



/* These macros are intended to be used to re-declare the uC/OS-II functions to be reentrant */
#define TASK_REENTRANT large reentrant
#define KCREENTRANT large reentrant

#include "os_cpu.h"
#include "os_cfg.h"



/* These Keil keywords are used in uC/OS-II source files. So the next macro definitions are needed */
#define data ucos51_data
#define pdata ucos51_pdata
#define xdata ucos51_xdata

#include "ucos_ii.h"
#include "os_kc51.h" /* MUST NOT REMOVE THIS LINE */


#endif

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Includes.h 화일은 우리가 사용하게될 헤더화일들을 모아놓은 화일입니다.

그러니 나중에 헤더화일들을 일일이 인클루드 시킬 필요없이 이 화일만 추가 시키면 됩니다.

처음에 __INCLUDES__ 를 선언해줌으로써 includes.h 라는 화일이 두번 호출되는걸 막는 부분으로 시작이 됩니다.

AT89S52의 레지스터가 정의된 REGX51.H 화일와 표준 라이브러리 헤더화일들을 인클루드 시켰습니다.



#define TASK_REENTRANT large reentrant
#define KCREENTRANT large reentrant



Keil 컴파일러는 재진입을 기본적으로 지원하지 않기에 재진입함수에는 large reentrant 라는 KEYWORD를 사용하게 됩니다.

large는 메모리 모델을 말하며, 외부 데이터 메모리에 reentrant 스택을 사용함을 말합니다.

재진입시에는 함수에서 선언된 데이터 변수들을 스택에 새로 잡아서 사용하게되며,

동일한 함수의 클론이 생성된다고 생각하면 됩니다.



reentrant가 지정되지 않게 되면, 함수의 재진입시, 진입 이전의 변수및 레지스터를 공유하게 되며, 컴파일시 재진입함수의 경고

메시지도 출력이 됩니다.



#define data ucos51_data
#define pdata ucos51_pdata
#define xdata ucos51_xdata



data, pdata, xdata 는 Keil 컴파일러의 KEYWORD로 사용되고 있지만, uCOS-ii 소스에서 사용되는 이름이기도 합니다.

그렇기 때문에 여기서 data라는 키워드가 사용되게 되면, 내부 데이터 영역을 가르키는게 아니라 ucos51_data라는

이름으로 변경하게 만들어서 uCOS-ii 소스의 변경없이 사용하도록 하였습니다.

(data 및 pdata 는 매개변수로 함수에 넘겨줄대 ucos-ii에서 동일한 이름으로 사용하고 있습니다.)

나중에 사용자가 data , pdata , xdata 라는 키워드를 사용하고 싶을때는 "os_kcdef.h" 헤더화일을 추가해서

data, pdata, xdata를 undef 시켜서 사용하도록 합니다.



다음 포팅기에서는 다른 화일들을 또 살펴보도록 하겠습니다.

화일들을 살펴보면서 조금씩 수정되는 부분이 있으니, 이렇게 조금씩 수정해 나가면서

전체 포팅화일들을 쭉~ 살펴보고, 프로젝트 화일에서 컴파일 옵션만 설정해주면....

포팅과정이 끝나게 됩니다...^^

 

< OS_KC51.H >

OS_KC51.h 헤더화일은 uCOS_ii.h 헤더화일에서 reentrant 한 함수들에 대해서 Keil 문법에 맞게

함수들의 프로토타입을 재정의해 놓았습니다.

이곳에서 재정의된 reentrant 함수들을 uCOS-ii 소스화일에도 똑같이 수정해 주어야 합니다.



....
#if OS_TASK_CREATE_EN > 0
INT8U OSTaskCreate(void (*task)(void *pd) KCREENTRANT, void *pdata, OS_STK *ptos, INT8U prio) KCREENTRANT;
#endif

...



OS_KC51.h의 일부분중 OSTaskCreate 함수가 KCREENTRANT 로 재정의가 되어있습니다.

(KCREENTRANT는 large reentrant 로 정의 되어있죠??)

그렇다면 우리는 OSTaskCreate함수가 정의되어있는 OS_TASK.c 화일에서

OSTaskCreate 함수에 KCREENTRANT를 추가해서 수정해 주어야 합니다.



이렇게 나머지 재정의 함수들에 대해서도 소스화일에서 KCREENTRANT 를 추가해주시기 바랍니다.

이부분에서만 uCOS-ii 소스 화일을 수정하시고 이후에는 절대 수정하시면 안됩니다...^^



< OS_CFG.H >

이 화일은 uCOS 환경설정 화일입니다.


#define OS_MAX_EVENTS 2 /* Max. number of event control blocks in your application ... */
/* ... MUST be > 0 */
#define OS_MAX_FLAGS 2 /* Max. number of Event Flag Groups in your application ... */
/* ... MUST be > 0 */
#define OS_MAX_MEM_PART 4 /* Max. number of memory partitions ... */
/* ... MUST be > 0 */
#define OS_MAX_QS 2 /* Max. number of queue control blocks in your application ... */
/* ... MUST be > 0 */
#define OS_MAX_TASKS 8 /* Max. number of tasks in your application ... */
/* ... MUST be >= 2 */


포팅소스의 OS_CFG.h 화일을 열어보시면 위와 같은 식으로 정의 되어있는 걸보실수 잇습니다

주석이 옆에 잘달려 있어서 어떤 정의를 하고 있는지 쉽게 알수 있습니다...^^



OS 이벤트, 태스크등의 갯수는 몇개로 할것이며, 어떤 함수들을 사용할것이냐 등등...

이부분의 설정은 커널사이즈에 영향을 미치게 되니, 찬찬히 살펴 보시면서 각각 설정해주시기 바라며

여기서는 포팅소스의 아랫부분에 정의된 클록 부분만 수정을 하도록 하겠습니다.



#define OS_TICKS_PER_SEC 50 /* Set the number of ticks in one second */


이부분은 OS_TICK을 1초에 몇번을 실행시키느냐 하는 부분인데...저희는 50으로 설정하도록 하겠습니다.

Tick은 1초에 50번 울리게 되며, 실행시마다 스케쥴링을 하게 됩니다.



< OS_CPU.H >



위 화일은 uCOS의 CPU 의존적인 설정이 들어있습니다.

uCOS에서 사용하게될 데이터 형들에 대한 정의와 크리티컬 영역의 진입 방법 설정하는 부분등이 있습니다.

많은 양의 코드가 들어있지 않기에 쑥 살펴 보시기 바랍니다.





이제 OS_CPU_A.a51 화일과 OS_CPU_C.c 화일만 남았습니다..

이 두화일이 가장 신중히 살펴보아야 할곳입니다.



<OS_CPU_C.C>

=============================================================================================================

OS_CPU_C.C 화일에서는 컴파일러및 MCU에 종속적인 C함수들을 정의하고 있습니다.



OS_STK *OSTaskStkInit (void (*task)(void *pd) KCREENTRANT, void * vd, OS_STK *ptos, INT16U opt) KCREENTRANT



OSTaskStkInit 함수가 있습니다. 이 함수는 OSTaskCreate 함수를 사용해서 태스크를 생성할때 호출되는 함수로서,

인자로 받는 스택에 가상의 초기 스택프레임을 만들어 줍니다.

처음 인자로 받는 void (*task)(void *pd) KCREENTRANT 는 KCREENTRANT로 선언된 함수의 포인터 입니다.

void (*task)(void *pd) 는 함수의 포인터를 가르키며, 인자로 포인터를 받는 함수 포인터 입니다.

(모르신다면, C언어 책을 참고해 보시길...)



그 다음 두번째 인자인 void *vd 는 *task 포인터가 받는 인자값을 받게 됩니다.

OS_STK *ptos 는 스택의 시작포인터를 넘겨 받습니다.

그리고 INT16U opt 는 태스크의 우선순위 값입니다.



그럼 스택의 모양을 어떻게 만드는지 보도록 합시다.

여기서 만들게 되는 스택의 모양은 태스크스위칭시에 스택을 푸쉬팝하는 모양과 같아야합니다. ^^

그러므로 이부분에서 스택의 모양을 잘잡아주어야 합니다.



OS_STK *OSTaskStkInit (void (*task)(void *pd) KCREENTRANT, void * vd, OS_STK *ptos, INT16U opt) KCREENTRANT
{
INT8U * stk;

opt = opt; /* 'opt' is not used, prevent warning */
stk = (INT8U *) ptos; /* Load stack pointer */

stk -= sizeof(void *); /* Save the vd to external stack */
*(void**)stk = vd; /* */

stk -= sizeof(INT16U); /* The value should be loaded to PC */
*(INT16U*)stk = (INT16U) task; /* next time when this task is running */



/* Following is the registers pushed into hardware stack */
*--stk = 'A'; /* ACC */
*--stk = 'B'; /* B */
*--stk = 'H'; /* DPH */
*--stk = 'L'; /* DPL */
*--stk = PSW; /* PSW */
*--stk = 0; /* R0 */

stk -= sizeof(void *); /* Keil C uses R1,R2,R3 to pass the */
*(void**)stk = vd; /* arguments of functions. */

*--stk = 4; /* R4 */
*--stk = 5; /* R5 */
*--stk = 6; /* R6 */
*--stk = 7; /* R7 */

/* Following is the registers pushed into hardware stack manually to support the dallas 390 */
*--stk = 0x80; /* IE, EA is enabled */
/* Next is calculating the hardware stack pointer. */
*--stk = (INT8U) STACK_START-1 /* Initial value when main was called */
+1 /* IE */
+8 /* R0-R7, eight registers was saved */
+5 /* ACC, B, DPH, DPL, PSW, five registers */
+sizeof(INT16U) /* The PC value to be loaded */
;

return ((void *)stk);
}



스택초기화 함수를 위와 같이 AT89S52에 맞게 변경하였습니다. 현재 저희가 받은 포팅소스는 Dallas390에 포팅된 소스라서

기본적으로 8051코어를 사용하지만 추가된 레지스터가 몇개 있고, 또 PC(Program Counter)를 Dallas390은 24바이트사이즈를

사용하는 독특한 구조라서 변경해야 할 부분이 몇군데 있었습니다. 위와 같인 심플한 모양으로 변경하시기 바랍니다.



먼저 스택에 ACC, B, DPH, DPL, PSW, R0 ~ R7, 그리고 IE레지스터의 순으로 넣게 됩니다.

R1,R2,R3 레지스터는 Keil에서는 함수의 아규먼트 부분으로 사용되며, R3, R2, R1을 차례대로 꺼낼때 (void *) 형의 vd 값이 호출되게 됩니다. vd는 Generic 한 포인터 형으로서 Keil에서는 Generic 한 포인터의 매개변수는 R1,R2,R3을 사용하게 됩니다. ^^

(이부분은 Keil C에서 제공하는 C51.pdf에 보면 잘나와있습니다.)



그리고 IE레지스터에서 EA인터럽트 인에이블을 허용한 상태로 넘기게 되는데요..

만약 이부분에서 인터럽트를 인에이블 시키지 않아놓으면, 태스크 스위칭할때 인터럽트가 디스에이블상태로

되기때문에 태스크스위칭한후에는 틱함수가 불리지 않게 되게 됩니다. 인에이블 시켜야하겠죠.



*--stk = (INT8U) STACK_START-1 /* Initial value when main was called */
+1 /* IE */
+8 /* R0-R7, eight registers was saved */
+5 /* ACC, B, DPH, DPL, PSW, five registers */
+sizeof(INT16U) /* The PC value to be loaded */
;



STACK_START 는 내부 데이터 변수로 선언된 스택의 시작번지를 가르키고 있으며, OS_CPU_A.a51 화일에 정의되어

있습니다. 그래서 extern 으로 설정되어있죠.



extern idata unsigned char STACK_START[1];



우리가 초기에 만든 스택 모양의 제일 마지막에는 외부에 정의된 스택의 내용을 내부스택에 넣기위해서 총 push된 스택의 크기가

저장되게 됩니다.



이부분이 가장 중요한 스택프레임 만드는 부분이였고, 다른 부분도 살펴보죠. ^^



extern INT8U xdata * data C_XBP;



앞으로 많이 보게될 C_XBP입니다. 이는 외부에 선언된 스택의 포인터입니다.




void OSStartHighRdy(void) KCREENTRANT
{
OSTaskSwHook();
OSRunning=1;

C_XBP=OSTCBHighRdy->OSTCBStkPtr;

LoadCtx();
}



이부분에서는 OSSTART 함수에서 호출되며, 현재 가장 우선순위가 높은 스택의 포인터를 C_XBP에 저장한 후에

Context Switching 을하게 됩니다.



void C_OSCtxSw(void) KCREENTRANT
{


OSTCBCur->OSTCBStkPtr = C_XBP;

OSTaskSwHook();
OSTCBCur = OSTCBHighRdy;
OSPrioCur = OSPrioHighRdy;

C_XBP = OSTCBCur->OSTCBStkPtr;
LoadCtx();
}



C_OSCtxSw(void) 함수는 C함수에서 호출하는 문맥전환 함수입니다. 이 함수도

문맥전환을 위해서 LoadCtx()함수를 호출하게 됩니다.



void OSIntCtxSw(void) KCREENTRANT
{
EA=0;
SP=SaveSP;

C_XBP=OSTCBCur->OSTCBStkPtr;

#pragma ASM
EXTRN CODE(_?KCOSCtxSw)
MOV A, #LOW _?KCOSCtxSw
PUSH ACC
MOV A, #HIGH _?KCOSCtxSw
PUSH ACC
RETI
#pragma ENDASM
}



OSIntCtxSw(void) 함수는 인터럽트 루틴에서 호출하는 문맥전환 함수입니다.

위 어셈명령어를 간단히 소개하자면, OS_CPU_A.a51에 정의된 _?KCOSCtxSw 함수의

주소를 ACC를 이용해서 push 한다음에 RETI 명령을 수행하게 되면,

KCOSCtxSw로 되돌아가게 됩니다. ^^



void OSTickISR(void) interrupt 1
{
/* Do this first */
OSIntNesting++; /* Increment ISR nesting level directly to speed up processing */
// OSIntEnter(); /* Must be called first at every hardware interrupt entry point */
if(OSIntNesting==1) /* Only at the outerest interrupt we do these. */
{
#pragma ASM
PUSH IE
#pragma ENDASM
EA=0;
SaveSP=SP;
OSTCBCur->OSTCBStkPtr=C_XBP; /* OSTCBCur->OSTCBStkPtr is free now, so it can be used to story the value of SP */
EA=1;
}
OSTimeTick(); /* Must be called during tick isr */
OSIntExit(); /* Must be called finally at every hardware interupt exit point */
if(OSIntNesting==0)
{
EA=0;
C_XBP=OSTCBCur->OSTCBStkPtr;
SP=SaveSP;
#pragma ASM
POP IE
#pragma ENDASM
}
}



OS_CPU_C.c 화일에는 Tick 인터럽트 함수가 선언되어있습니다.

void OSTickISR(void) 함수가 바로 Tick 함수로서 이부분에서 OS Tick 을 증가시켜,

스케쥴링함수를 호출합니다.



void SerialIntr(void) interrupt 4
{
/* Do this first */
OSIntNesting++; /* Increment ISR nesting level directly to speed up processing */
// OSIntEnter(); /* Must be called first at every hardware interrupt entry point */
if(OSIntNesting==1) /* Only at the outerest interrupt we do these. */
{
#pragma ASM
PUSH IE
#pragma ENDASM
EA=0;
SaveSP=SP;
OSTCBCur->OSTCBStkPtr=C_XBP; /* OSTCBCur->OSTCBStkPtr is free now, so it can be used to story the value of SP */
EA=1;
}


/* Next you can do something for yourself. */
/* Finally, before exit from ISR, must do as following. */
OSIntExit(); /* Must be called finally at every hardware interupt exit point */
if(OSIntNesting==0)
{
EA=0;
C_XBP=OSTCBCur->OSTCBStkPtr;
SP=SaveSP;
#pragma ASM
POP IE
#pragma ENDASM
}
}


void SerialIntr(void) 함수는 포팅한 사람이 만들어 놓은 시리얼통신 인터럽트 프로그램으로

사용하셔도 되고 지우셔도 되고 편하신대로 하시면됩니다.

사용자 인터럽트를 정의할때는 OSIntEnter() 함수와 OSIntExit() 함수를 사용하시고, OSIntNexting 변수로

중첩 인터럽트에 대한 정보를 처리하시면 됩니다.

참고하자면, 위에서 설명한 Tick 함수나 시리얼 인터럽트 서비스루틴에서는 OSIntEnter 함수의 사용대신에

직접 OSIntNesting 변수를 증가시켜주었죠?? 좀더 속도향상을 위해서...



OS_CPU_C.c 화일에는 이외에 Hook함수가 몇개 정의 되어있는데,

빈껍데기에 불과하고, 사용자가 필요할때 정의해서 쓰시면됩니다.



위에서 제가 Dallas390으로 포팅된 소스를 전부 변경했습니다. 위에서 설명하지 않은 함수들은

그냥 변경없이 쓰시면됩니다.

^^ 별로 크게 바꾸거나 변경한 부분은 없기 때문에 쭉 살펴보시기 바랍니다.

그럼 이상 OS_CPU_C.c 화일에 대한 설명은 마치고

다음에는 OS_CPU_A.a51 함수도 좀 손을 보도록해서 포팅기의 마지막으로 가도록 하겠습니다.

 

<OS_CPU_A.a51>

=================================================================================================================


$NOMOD51


OS_CPU_A.a51 소스의 제일 위에는 $NOMODE51 이라는 지시어가 적혀있습니다.

이를 주석처리를 하거나 지워주시기 바랍니다.

Dallas390은 어셈블리어를 컴파일할때 AX51을 사용하였고, AX51은 Pre-Define 된 8051 레지스터들을 사용하지 않고,

자체적으로 사용하기에 위 옵셥을 줘야했지만, AT89S52는 A51 컴파일러를 사용할것이기에 위 옵션을 생략하도록 하겠습니다.



PUBLIC ?C_XBP, C_XBP

EXTRN CODE(_?C_OSCtxSw)

PUBLIC _?LoadCtx, STACK_START, _?OSCtxSw, _?KCOSCtxSw



LoadXBP MACRO
MOV DPH, C_XBP
MOV DPL, C_XBP+1
ENDM

SaveXBP MACRO
PUSH IE
CLR EA
MOV C_XBP, DPH
MOV C_XBP+1, DPL
POP IE
ENDM

LoadREG MACRO REG
MOVX A, @DPTR
MOV REG, A
ENDM

SaveREG MACRO REG
MOV A, REG
MOVX @DPTR, A
ENDM

; The PUSHA now emulates the pushing sequence what Keil C does.
PUSHR MACRO
IRP REG, <ACC, B, DPH, DPL, PSW, 0, 1, 2, 3, 4, 5, 6, 7>
PUSH REG
ENDM
ENDM

POPR MACRO
IRP REG, <7, 6, 5, 4, 3, 2, 1, 0, PSW, DPL, DPH, B, ACC>
POP REG
ENDM
ENDM



위 소스들은 OS_CPU_A.a51에서 사용하게될 메크로 들입니다.

LoadXBP 메크로는 외부스택포인터(C_XBP)의 포인터를 DPH, DPL에 로드하는 메크로입니다.

외부에 우리가 잡아놓은 테스크스택의 위치를 가져와서 내부스택으로 Context 를 옮기는 경우에 사용됩니다.

SaveXBP는 LoadXBP의 반대이겠죠?? ^^

LoadREG는 REG에 매개변수로 받은 레지스터에 DPTR에서 로드되는 값을 넣게 됩니다.

SaveREG는 역시 그 반대죠...



PUSHR 과, POPR 메크로는 Context Swithing 할때, 스택에 푸쉬하고 팝하는 이중메크로입니다. ^^

저 순서는 우리가 OS_STK *OSTaskStkInit 함수에서 잡아주었던 스택의 모양과 동일해야겠죠??



; Declare the external stack pointer by ourself, so that we can use it freely.
; you know, in the name of '?C_XBP' it can not be used in c modules but in the
; name of 'C_XBP' it can do.

DT?C_XBP SEGMENT DATA
RSEG DT?C_XBP
?C_XBP: ; These two labels point to the same address
C_XBP: ;
DS 2

; Declare a label 'Stack' in the hardware stack segment so that we know where it begins.
?STACK SEGMENT IDATA
RSEG ?STACK
STACK_START:
DS 1



C_XBP는 외부 데이터에 위치한 태스크스택의 주소값을 저장하는 공간으로 사용되고,

STACT_START 는 내부 스택포인터의 위치를 갖게 됩니다.

좀더 부연 설명을 하자면,

?C_XBP를 위해서 DT_?C_XBP라는 재배치가능한 세그먼트를 생성한후에

?C_XBP의 값을 스타트업 코드에서 넘겨받게 됩니다. (스타트업 코드 참조...)

?C_XBP나 C_XBP나 동일한 값이며, ?C_XBP는 C함수에서는 호출이 불가능하므로, C_XBP를 C에서 사용하게됩니다.

(위에 주석이 잘되어있죠?? ^^_

물론 STACK_START도 같은 식이죠....





; Load context from the external stack pointed by C_XBP
PR?LoadCtx SEGMENT CODE
RSEG PR?LoadCtx
_?LoadCtx:
LoadXBP ; Load the C_XBP to DPTR

LoadREG SP ; Load the hardware stack pointer
INC DPTR ;

MOV R0, SP ; Now we pop the hardware stack
LC_1: ; from the external one.
LoadREG @R0 ; Did not use the PUSH ACC instruction for if we want to
INC DPTR ; do so, we have to DEC DPTR, which costs much.
DEC R0 ;
CJNE R0, #STACK_START-1, LC_1 ;

SaveXBP ; after the hardware stack has been popped,
; the external stack pointer should be adjusted

RestoreCtx:
WANTFASTER EQU 1
$IF WANTFASTER
POP PSW ; A little bit dangerous but it works. C is PSW.7 and EA is IE.7
MOV EA, C ; They are at the same bit location of a byte.
$ELSE
POP ACC ; Safe way to do the same thing.
RLC A ;
MOV EA, C ;
$ENDIF
; Now that the context has been loaded into hardware
POPR ; stack, what we need do is just popping them upto registers.
;

RET ; Now everything is ready, a RET will bring the task to run.

; Task level context switch entry point, which is intended to be called by task gracefully.
_?OSCtxSw:
PUSHR ; Save current context first into hardware stack
PUSH IE

_?KCOSCtxSw: ; Now begin pushing hardware stack to external one
LoadXBP ; Load the external stack pointer first to prepare storing
; data into it.

MOV A, SP ; Calculate how much memory in external stack needed
CLR C ; so that we can adjust the external stack pointer
SUBB A, #STACK_START-1 ; Calculated the length of hardware stack

MOV R0, A ; Save the length of hardware stack to R0, which is used as a counter on saving hardware stack.

NC A ; Add the space for storing SP

CLR C
XCH A, DPL ; Now ACC contains the right amount of external stack memory should be used.
SUBB A, DPL ; Adjust the external pointer.stored in DPTR to make to point to the new stack top

; from where we will store hardware stack.
JNC SC_1
DEC DPH
SC_1:
MOV DPL,A ; Now DPTR contains the external stack pointer after pushing context into external stack.

SaveXBP ; Save to external stack pointer.
; Keeps the DPTR containing the external stack pointer still.
SaveREG SP ; Save hardware stack pointer in the top of external stack

SC_2:
INC DPTR ;
POP ACC ; Pop the data from hareware stack
MOVX @DPTR, A ; and save into external one.
DJNZ R0, SC_2 ; Remember, R0 contains the hardware stack's length.

LJMP _?C_OSCtxSw ;




위 소스가 수정된 소스입니다...^^

달라진 부분은 별루 없구요... AX51문법으로 된 #BYTE0 (STACK_START-1) 이런문법을 바꾼거 밖엔 없네요...



그외에 위에 메크로 조금 수정한거...^^

문맥의 저장, 로드, 스위칭에 관련된 부분인데, 상당히 짧기에 별설명없이 쭉 훍어보시면 이해가가시리라 믿습니다.

대략 설명하자면,

_?LoadCtx: 함수같은 경우는 외부스택에 저장된 문맥들을 내부스택으로 옮겨와서,

POPR 메크로를 사용해서 태스크스위칭을 하게되는 부분입니다. ^^

_?KCOSCtxSw 같은 경우는 그 반대이구요... ^^

쭉 명령어들을 따라가시면서 분석해보세요...어렵지 않습니다...



그럼 이걸로 OS_CPU_A.a51 화일도 끝이 났네요...



그럼 이제 다음 포팅기에서는 이제까지 살펴본 화일들을 이용해서, 실제로 포팅을 해보는 일만 남았네요.



정말 짧게 쓰고자했던 포팅기였는데 생각보다 넘 길어져서...당황스럽네요...^^;



그럼 다음에는 스타트업코드를 살짝쿵 살펴보고 마무리를 짓죠...

이제 해야할 일은, 지금까지 AT89S52에 맞게 수정했던 화일들을 가지고,

스타트업코드와 함께 컴파일 하는 일만 남았군요....^^



스타트업코드는 Keil 컴파일러에서 기본적으로 제공해주는 코드를 사용하도록 하겠습니다.



Keil 컴파일러가 설치되어있는 아래 폴더에서 STARTUP.A51 화일을 우리가 작업하는 폴더로 복사하시기 바랍니다.

C:\Keil\C51\LIB\STARTUP.A51



이제 STARTUP.A51 에서 우리가 살펴보아야 할부분만 보도록 하죠~



<STARTUP.a51>

=================================================================================================================

; ; the absolute start-address of IDATA memory is always 0
IDATALEN EQU 80H ; the length of IDATA memory in bytes.

XDATASTART EQU 8000H ; the absolute start-address of XDATA memory
XDATALEN EQU 8000H ; the length of XDATA memory in bytes.

PDATASTART EQU 0H ; the absolute start-address of PDATA memory
PDATALEN EQU 0H ; the length of PDATA memory in bytes.


스타트업 코드를 보시면 위와 같이 데이터 영역의 크기와 시작 번지를 지정하는 부분이 있습니다.

IDATALEN은 내부 데이터의 크기를 나타내고, 시작번지는 언제나 0번지라고 친절하게 주석이 달려있죠??

XDATASTART 외부데이터의 시작번지는 8000번지로 잡겠습니다.(8000번지부터 FFFF번지까지 램영역으로 잡자.)

XDATALEN 외부데이터의 크기는 32Kbyte인 8000h로 설정하겠습니다.

PDATA 영역은 우리는 사용하지 않기때문에 0으로 설정해둡니다.



; Stack Space for reentrant functions in the SMALL model.
IBPSTACK EQU 0 ; set to 1 if small reentrant is used.
IBPSTACKTOP EQU 0FFH+1 ; set top of stack to highest location+1.
; Stack Space for reentrant functions in the LARGE model.
XBPSTACK EQU 1 ; set to 1 if large reentrant is used.
XBPSTACKTOP EQU 0FFFFH+1 ; set top of stack to highest location+1.
; Stack Space for reentrant functions in the COMPACT model.
PBPSTACK EQU 0 ; set to 1 if compact reentrant is used.
PBPSTACKTOP EQU 0FFFFH+1 ; set top of stack to highest location+1.



이부분은 이제 c에서 사용할 reentrant 펑션을 위한 스택공간을 설정하는 부분입니다.

역시 주석에 따라서 설정을 위와같이 해주시면 됩니다.

XBPSTACK 의 값을 1로 했을때, 사용하겠다는 의미이고, XBPSTACK은 Large 메모리 모드에서

사용하는 외부스택입니다.



그 위에 쭉 스타트업 코드를 살펴보시면, 메모리를 초기화 하는 부분이 있고,

마지막에 스택포인터를 설정해주고 C함수의 main으로 점프를하게됩니다.


IF XDATALEN <> 0
MOV DPTR,#XDATASTART
MOV R7,#LOW (XDATALEN)
IF (LOW (XDATALEN)) <> 0
MOV R6,#(HIGH (XDATALEN)) +1
ELSE
MOV R6,#HIGH (XDATALEN)
ENDIF
CLR A
XDATALOOP: MOVX @DPTR,A
INC DPTR
DJNZ R7,XDATALOOP
DJNZ R6,XDATALOOP
ENDIF

위 코드가 외부 데이터를 초기화 하는 부분이죠...^^



IF XBPSTACK <> 0
EXTRN DATA (?C_XBP)

MOV ?C_XBP,#HIGH XBPSTACKTOP
MOV ?C_XBP+1,#LOW XBPSTACKTOP
ENDIF



이코드는 EXTERN 으로 선언된 ?C_XBP에 위에서 설정해주었던, XBPSTACKTOP의 값을

넣어주는 부분입니다.

?C_XBP는 우리가 OS_CPU_A.a51 화일에서 보았었죠?? ^^

이곳에서 그 값을 넣어주게 됩니다.



MOV SP,#?STACK-1

그리고 마지막 부분에는 이런식으로 내부데이터메모리에서 스택스그먼트의 시작주소를

스택에 집어넣고 C로 점프하게 됩니다.

8051에서 스택의 위로 자라게 되죠... 그러므로 ?STACK 세그먼트가 자리잡게 되는 위치는

내부데이터의 마지막 부분에 자리잡게 됩니다.

============================================================================================

이제 스타트업 코드도 살펴보았고 컴파일을 해보도록 하겠습니다.





그럼 Keil 프로젝트 화일을 만들어서 아래와 같은 화일들을 등록하시기 바랍니다.

=================================

uCOS_ii.C

OS_CPU_C.c

OS_CPU_A.a51

main.c

task.c

STARTUP.a51

=================================

uCOS_ii.C 화일에는 ucosii 소스화일들이 인클루드되어 있어서, uCOS_ii.c 화일을 컴파일하게 되면,

uCOSii 소스화일들이 전부 컴파일이 됩니다.

그리구 우리가 포팅한 OS_CPU_C 와 OS_CPU_A 화일도 등록을 해주시고요...

그리고 제가 간단하게 만들어놓은 task.c 화일도 등록을 해놓겠습니다.

task.c 화일은 간단하게 태스크들을 정의해 놓았습니다.



이제 프로젝트 옵션에 가셔서 프로젝트 옵션을 설정해야합니다.

Device : AT89S52 (LX51, AX51 은 사용하지 않습니다.)

Target : 메모리 모델과 코드 모델은 LARGE 입니다.

offchip 코드 메모리는 0x0000 ~ 0x7ffff

offchip 데이터 메모리는 0x8000 ~ 0xffff

C51 : 컴파일 레벨은 정당히 설정하세요... ^^

A51 : SFR names는 Define 시킵니다.

BL51 : 타겟 다이알로그에 있는 세팅을 사용합니다.



이제 컴파일 하신후에 디버깅 모드에서

멀티태스킹이 되시는 모습을 보시기 바랍니다. ^^

Keil 컴파일러에서 제공하는 시뮬레이터는 실제와 상당히 정확해서...

시뮬레이터만 이용해도 디버깅하기에 충분합니다. 만약 생각되로 잘 움직이지 않는다면,

디버깅 모드에서 Step in 으로 찬찬히 살펴보시면 어디가 잘못된지 금방 보일테니...

끈기를 가지고 포팅해보시기 바랍니다.



이상으로 포팅기를 마치고, 포팅된 소스들을 전부 모아서 압축화일로 등록을 해놓겠습니다.



가끔씩 생각날때, 짬날때 쪼금씩 적다가 보니...

중간 중간 빼먹은게 있지 않나 생각도 되고, 잘못된 부분도 있을거 같네요...^^



잘못된 부분이 있거나 의문되시는 부분은 직접 리플이나, 메일로 연락주시면, 바로 수정하거나

답변을 드리도록 하겠습니다.



그럼 즐거운 포팅되세요~



 


 

 

'c or linux' 카테고리의 다른 글

How to be a Programmer : A short....  (0) 2006.07.20
C를 C++처럼 사용하기  (0) 2006.07.20
좋은 구조체 리턴 함수  (0) 2006.04.15
&, && 및 우선순위 주의  (0) 2006.03.28
영어 축약어  (0) 2006.02.15
Posted by '김용환'
,

구조체를 리턴해야 하는 경우는 다음의 경우를 이용하는 것이 좋다.

 

출처: http://kin.naver.com/db/detail.php?d1id=1&dir_id=10104&eid=0tBeloUK0KfHnV5fAL4TxnUrkk8EdSo/ 

 


#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#define Link_Num 18

//구조체 선언
typedef struct _arc
{
    int n1; //노드 x
    int n2; //노드 y
    int value; //링크수준
}Arc;


//함수 선언
Arc* Empty_Arc(void);

int main()
{
    int i;
    Arc *temp_arc;

    temp_arc = Empty_Arc();


    for(i=0 ; i < Link_Num ; i++)       
 printf("체크 %d\n", temp_arc[i].value);  //결과2

    free(temp_arc);
    system("pause");
    return 0;
}

Arc* Empty_Arc(void)
{
    int i, j;
    Arc* temp = (Arc*)malloc(sizeof(Arc) * Link_Num);

    //초기화
    for (i=0 ; i < Link_Num ; i++)    {       
 temp[i].n1 = 0;
        temp[i].n2 = 0;
        temp[i].value = i;
    }

    for(i=0 ; i < Link_Num ; i++)       
 printf("리턴값 %d\n", temp[i].value);  //결과1

    return temp;
}

Posted by '김용환'
,

1. 산술연산 &와 논리 연산 &&의 의미를 파악해야 한다.

if ((ptr[1] & 0xFF) == 0x07) { (0)

if ((ptr[1] && 0xFF) == 0x07) {   (X)

 

2. 우선순위가 ==이 &보다 높다.
if ((ptr[1] & 0xFF) == 0xF8) { (0)
if (ptr[1] & 0xFF == 0xF8) { (x)

 

Posted by '김용환'
,