2011. 9. 14. 16:13

[ 011 ] - 공백문자를 사용하여 코드를 보기 좋게 만들자.

프로그래밍을 시작한 지 얼마 안 되는 사람들은 소스 코드를 작성하기에 급급해서 다음 코드와 같은 형태로

구성하는 습관이 있습니다.

위의 코드에서 공백문자만 추가하면 다음과 같습니다.

연산자와 변수 사이에 단순히 공백문자 하나만 더 추가했을 뿐인데도 위의 코드보다 훨씬 보기가 편할 것입니다.

사실 실행 결과는 위의 코드나 아래 코드나 똑같지만 소스 코드를 작성하는 사람들의 의무 가운데 다른 사람이

자신의 소스 코드를 보더라도 쉽게 이해할 수 있도록 해야 한다는 조항이 있다는 것을 명심하세요.

 

[ 012 ] - 변수를 사용할 때는 수직으로도 정렬하자.

이 방법도 신입 사원들에게 제가 권장하는 방법 중의 하나입니다.

위와 마찬가지로 이렇게 하지 않아도 사실 상관은 없지만 이것은 소스 코드를 한눈에 보기에 더 좋은 방법입니다.

소스 코드를 수직으로 줄을 맞춘다는 의미는 보통 변수들, 특히 구조체의 멤버나 구조체 포인터를 사용하여

멤버의 값을 초기화할 때 각각의 이름의 길이에 따라 소스 코드가 들쑥날쑥 하게 됩니다.

이런 경우가 한두 줄이면 그다지 상관없겠지만 그 수가 좀 많아지면 보기에 안 좋겠죠.

다음은 수직으로 정렬되지 않은 소스 코드입니다.

대략 위와 같은 코드가 있다고 가정하면 코드에는 큰 문제는 없어 보입니다.

그러나 위의 코드를 다음과 같이 수직으로 정렬하면 어떨까요?

물론 수직으로 정렬할 때도 <Spacebar> 가 아닌 <Tab> 으로 열을 맞춥니다.

위의 코드는 그다지 길지 않기 때문에 특별히 표가 나지는 않지만, 실무에서 이와 비슷한 작업을 하다 보면

초기화 변수만 수십 개를 쓰는 경우도 생깁니다. 이런 경우에는 위의 코드와 같이 수직으로 정렬을 해두면

나중에 코드를 보거나 디버깅할 때 도움이 됩니다.

 

[ 013 ] - 전역 변수의 사용을 최대한 피하자

전역 변수도 나름대로의 장점이 있기 때문에 필요한 경우에는 사용해야 하지만 되도록이면 매개변수나

구조체를 이용하여 전역 변수의 기능을 대신하는 것이 좋습니다. 지금보다 나은 실력의 프로그래머가 되려면

전역 변수의 사용을 최대한 피하는 것이 좋은 프로그래밍 습관입니다.

 

[ 014 ] - 정적 변수를 활용하자.

우리가 일반적으로 알고 있는 static의 기능 외에 다른 파일의 변수의 값을 변경하거나 아무 제약 없이

접근하는 것을 막을 수 있습니다. 이와 같은 기능을 C++나 자바와 같은 객체 지향 프로그래밍에서 private이라는

키워드로 제공합니다. 비록 C 언어에서 완벽하지는 않아도 정적 변수를 사용하면 다른 파일이 변수 값을 수정할

때 일어날 수 있는 버그나 오류를 막을 수 있습니다.

예를 들면 앞의 예제에서 SetAge()와 같은 함수는 사람의 나이를 설정하는 기능이므로 음수가 절대 있을 수 없습니다.

이때 다음과 같이 SetAge() 함수를 수정하면 무의식적으로 저지를 수 있는 실수를 막을 수 있습니다.

위의 코드처럼 매개변수 num의 값이 0보다 큰 경우에만 정적 변수 Age에 대입하고 그렇지 않은 경우에는

에러 코드를 출력하게 할 수 있습니다.

 

[ 015 ] - 정적 함수로 접근 권한을 주자.

정적 변수와 마찬가지로 정적 함수를 사용하면 함수가 정의된 파일 안에서만 함수를 사용할 수 있고

다른 파일에서 접근하는 것을 허용할 수 있습니다. 이와 같은 방법은 프로그래밍의 모듈화를 극대화할 수 있으며

디버깅에 들어가는 시간과 노력을 줄일 수 있습니다.

많은 사람들이 C++가 C 언어보다 우수하다고 말하는 이유 가운데 하나가 바로 접근 권한을 줄 수 있기 때문인데,

실제 C 언어에서도 완벽하지는 않지만 파일(모듈) 단위로 접근 권한을 줄 수 있습니다.

 

[ 016 ] - 변수, 함수 이름은 누구나 이해하기 쉽게 만들자.

여러 명이 공동으로 프로젝트를 진행하는 경우에는 함께 프로젝트에 참여하는 사람들이 미리 이름 규칙을 협의하는

것이 좋습니다. 묵시적으로는 헝가리언 표기법을 따르겠지만 프로젝트의 성격에 따라 특별히 이름을 정하는 것에 대한

규칙을 정해 두는 것도 좋습니다.

예를 들면 플래그 기능으로 사용하는 변수는 f를 사용하며, 포인터는 p, char형의 경우는 변수 이름 앞에 c를 붙이고 문자열인 경우는 s를 붙인다고 미리 협의를 하면 각 파트에서 이름을 결정할 때 도움이 될 수 있습니다.

이와 같은 형식을 사용하면 변수 이름만 보고도 그 변수의 자료형을 예측할 수 있기 때문에 자료형이 달라서 생기는 오류나 버그를 미리 막을 수 있습니다.

 

[ 017 ] - 같은 자료형을 서로 다른 목적으로 사용하려면 typedef를 이용해서 새로운 자료형으로 정의해서 사용하자.

C 언어에서 제공하는 자료형은 한계가 있기 때문에 서로 다른 목적으로 사용하는 변수라고 해도 같은 자료형으로

데이터를 표현하기가 쉽습니다. 대표적인 것이 정수형인 int형과 문자형인 char형입니다.

소스 코드가 복잡하지 않고 길지 않으면 같은 자료형을 사용해도 크게 상관이 없지만, 소스 코드가 복잡하고

소스 코드 파일의 개수가 많을 때에는 같은 자료형을 사용하면 아무리 변수의 이름을 쉽게 표현해도 프로그램을

이해하기 어렵습니다.

이때 문제를 해결하기 위해서 사용할 수 있는 것이 typedef입니다.

다음과 같이 같은 정수형, 문자형이라도 필요한 목적에 맞게 정의해서 사용하면 소스 코드를 이해하기가 훨씬 쉽습니다.

 

 

[ 018 ] - const 포인터의 숨겨진 기능을 알아 두자.

C 언어에서 제공하는 const는 단지 상수 값을 사용하는 기능만 있는 것은 아닙니다.

특히 포인터와 함께 const를 사용할 때는 다음과 같은 숨겨진 기능이 있습니다.

 

① const 포인터가 가리키는 값을 변경할 수 없다. 단 const 포인터의 주소는 변경할 수 있다.

② const 포인터는 일반 포인터로 할당될 수 없다.

 

이 두 가지 특징은 포인터를 사용할 때 생길 수 있는 논리적인 버그를 어느 정도 예방할 수 있기 때문에 꼭 기억해 두세요.

 

[ 019 ] - 메모리 주소를 출력할 때에는 %#x 형식 지정자를 사용하자.

포인터의 주소를 출력할 때 다음과 같이 printf() 함수 안의 형식 지정자를 "%#x"로 사용하는 경우가 있습니다.

printf("NormalPtr[%#x] : %s\n", NormalPtr, NormalPtr);

보통 초보 프로그래머의 경우 포인터의 주소를 출력할 때 %p를 사용합니다.

%p의 경우는 16진수 형태로 포인터의 주소를 출력하라는 의미입니다.

따라서 %p를 사용하여 출력하면 다음과 같은 결과가 나타납니다.

NormalPtr[00424678] : RYAN

이때 %#x를 사용하면 다음과 같은 결과가 나타납니다.

NormalPtr[0x424678] : RYAN

%p를 사용하는 것보다 %#x를 사용하는 것이 포인터의 주소를 출력하는 데 훨씬 보기가 편하다는 사실을 알 수 있습니다. 또 어떤 경우에는 %#x 대신 %x를 사용하기도 하는데 %x를 사용하면 다음과 같은 결과가 나타납니다.

NormalPtr[424678] : RYAN

세 개의 형식 지정자 모두 포인터의 주소를 출력하는 형식으로 사용하지만 위의 세 가지 표현 가운데

실제 프로그래밍에서 사용하는 메모리 주소의 형식과 가장 맞는 표현이 0x424678의 형태이기 때문에

%#x를 사용하는 것이 좋습니다.

 

[ 020 ] - 함수를 사용할 때 좋은 습관 다섯 가지를 지키자.

함수가 얼마나 중요한 역할을 하는지 프로그래밍을 다루다 보면 뼈저리게 느낄 수 있습니다.

하지만 어떤 규칙도 없이 함수를 사용하면 소스 코드의 양이 많아지고 프로그램이 복잡해져서 상당한 곤란을 겪습니다.

따라서 다음 다섯 가지 습관을 지키어 함수를 사용하세요.

 

① 프로토타입을 명시하자.

② 반환 타입을 정확하게 기입하자.

③ 함수의 이름은 기능을 알기 쉽게 작성하자.

④ 함수의 매개변수를 잘 활용하자.

⑤ 하나의 함수는 한 가지 기능만 구현하자.

 

Posted by devanix
2011. 9. 14. 16:11

[ 001 ] - 모듈 ∙ 인터페이스 등을 확실하게 정의한 후 시작하자.

프로그래밍을 시작하기 전에 프로젝트에 참여하는 다른 프로그래머들과 충분히 의논하고 인터페이스를 확인하는

작업을 반드시 거쳐야 합니다.

 

[ 002 ] - 상태별로 제어하는 습관을 기르자.

실제 기업체나 연구소에서 상태 제어 프로그래밍 방법을 이용하여 실무가 이루어지고 있으므로 상태별로 제어하는 습관을 기르는 것이 중요합니다. 대부분의 프로그램은 여러 상태(status)로 이루어져 있고 각각의 상태를 제어하는 것은 일반적인 프로그래밍 방법 가운데 하나입니다.

 

[ 003 ] - 일어날 수 있는 모든 입출력을 점검하자.

프로그램을 설계할 때에는 일어날 수 있는 모든 입출력을 점검해야 합니다. 전체 몇 가지의 경우의 수가 일어날 수 있는지 확인하고, 어떤 경우에만 해당하는 사항인지 아니면 모든 경우에 대해서 대응해야 하는지 점검해야 합니다.

 

[ 004 ] - 프로젝트의 단계를 하나씩 밟자.

① 전체적인 구조 파악

② 세부적인 알고리즘 검토

③ 실제 구현

④ 디버깅과 수정

 

각 단계 가운데 어느 하나라도 그냥 지나치지 않아야 하며, 앞으로 일어날 수 있는 문제가 무엇인지 예측하고

미리 보완하는 습관을 가져야 합니다.

 

[ 005 ] - 필요하면 개발 환경을 직접 만들 수 있어야 한다.

어디에도 현재 진행하는 프로젝트에 최적화된 개발 환경은 없습니다. 프로젝트를 진행하면서 필요에 따라 프로그램을 테스트하거나 검증할 수 있는 도구를 직접 만들 수 있다면 이것은 전체적인 생산성을 높여 줄 것입니다.

 

[ 006 ] - 프로그램은 결과도 중요하지만 과정도 중요하다.

초보 프로그래머가 쉽게 범하는 실수 가운데 하나는 프로그램은 결과가 중요하다고 생각하는 것입니다. 물론 원하는 결과가 제대로 나오는 것도 중요하지만 결과가 제대로 나오더라도 프로그램이 엄청나게 많은 메모리를 차지하거나 프로그램의 실행 시간이 밥 한 끼를 먹어도 충분할 만큼 길다면 결코 좋은 프로그램을 만들었다고 볼 수 없습니다. 따라서 프로그램은 결과도 중요하지만 다음과 같은 중간 과정도 중요합니다.

 

① 코드의 최적화

메모리, CPU의 시간과 같은 리소스(resource)를 낭비하지 않고 적절하게 사용하는 최적화

② 코드의 재사용성

다른 사람이 자신의 코드를 사용할 때 별다른 수정 없이 사용할 수 있도록 구성하는 모듈화

③ 코드의 가독성

다른 사람이 소스 코드를 쉽게 이해할 수 있게 하는 가독성

 

[ 007 ] - if 문과 case 문을 적절하게 섞어서 사용하자.

if 문과 case 문은 서로 비슷한 기능을 하지만 사용하다 보면 서로 다른 기능을 할 때가 있습니다.

특히 수십 개의 if 문이 필요할 때에는 오히려 case 문이 더 적합한 경우가 있기 때문에 프로그램의 효율성을 위해서

프로그램 코드에서 if 문과 case 문을 섞어서 사용하는 습관을 갖는 것이 좋습니다.

 

[ 008 ] - 헤더 파일을 한 번만 인클루드하는 노하우를 익히자.

여러 사람이 공동으로 하나의 프로젝트를 진행하다 보면 헤더 파일을 인클루드하는 문제가 자주 생깁니다.

따라서 다음의 두 가지 방법 가운데 하나를 이용하면 인클루드 문제 때문에 일어나는 링크 에러를 막을 수 있습니다.

 

① #ifndef ~ #ifdef ~ #endif 방법

② #pragma once 방법

 

위의 두 방법 가운데 첫 번째 방법을 이용하면 컴파일러에 상관없이 어느 개발 환경에도 적용할 수 있기 때문에 나중에 다른 플랫폼에 소스 코드를 쉽게 이식할 수 있습니다.

 

[ 009 ] - 당근과 채찍을 두루 사용하는 프로젝트 매니저가 되자.

프로젝트 매니저는 근태 관리나 하고 아랫사람의 납기를 다그치기만 하는 군대의 고참과 같은 존재가 결코 아닙니다. 프로젝트에서 각각의 담당자들이 맡고 있는 분야 전체를 꿰뚫고 있고, 그 프로젝트에서 생길 수 있는 문제점을 예상하고,

그에 대한 대비책을 마련해야 하는 중요한 역할을 하는 사람이 프로젝트 매니저입니다.

따라서 프로젝트 매니저는 인간관계 뿐만 아니라 기술적인 노하우도 다른 프로그래머에 비해 탁월해야 합니다.

그저 시간이 흘러서 대리가 과장이 되고, 과장이 차장이 되는 그런 직책이 절대 아닙니다.

오히려 하나의 프로젝트를 진행하면서 가장 바쁘고, 힘들게 일하는 사람이 바로 프로젝트 매니저입니다.

또 프로젝트 매니저는 하나의 프로젝트를 성공적으로 끝내기 위해서 각 담당자들 개개인의 역량을 파악한 다음

업무를 진행시켜야 하며 그와 함께 적절한 칭찬을 해주어야 합니다. 실제 엔지니어들이 업무에서 갖는 가장 큰 불만은

칭찬에 인색한 프로젝트 매니저라고 합니다. 업무의 결과에 대해 정확한 당근과 채찍은 필수입니다.

채찍만 휘둘러 대는 프로젝트 매니저의 업무 습관은 프로젝트가 실패로 향하는 지름길입니다.

 

[ 010 ] - 주석은 필요한 정보만 간단하게 서술하자.

음식도 질에 상관없이 양만 많으면 언젠가는 탈이 나게 마련이듯이 프로그래밍도 마찬가지입니다.

쓸데없이 양만 많은 주석은 오히려 소스 코드의 이해에 방해를 줄 뿐만 아니라 소스 코드를 수정할 때에도 많은 주석을 함께 수정해야 하므로 번거롭기만 합니다.

소스 코드의 신빙성과 효율성을 위해서 보여주기만을 위한 소설식 주석보다 좀더 기능에 치중해서 간결하게 설명하는 것이 좋습니다.

주석을 작성할 때 다음과 같은 정보만 전달하는 것이 가장 효과적입니다.

- 현재 작업하고 있는 모듈 이름

- 모듈의 인터페이스(매개변수, 반환 타입 등)

- 모듈의 최초 작성자

- 모듈의 마지막 수정자

- 모듈의 작성 날짜와 가장 최근의 수정 날짜

그 밖에 소스 코드 안에서 코드 옆에 붙이는 주석은 필요한 경우에 함수 호출 부분에서 그 함수의 기능을 간단하게

나타낼 수도 있으며 특별히 중요한 변수인 경우에는 주의를 요한다는 설명을 곁들일 수 있습니다.

Posted by devanix
2011. 9. 14. 16:10

 

[ 책 소개 ]

코드는 짧게, 용량은 작게, 속도는 빠르게! 작은 차이로 큰 효과를 내는 좋은 프로그래밍 습관 61가지를 알려주는 책. 초보 프로그래머들이 프로그래밍 전문가로 거듭나는 데 도움을 줄 것이다. 

특히 코드의 간결성, 재사용성, 최적화, 메모리 관리, 에러 방지 등 실무에 꼭 필요한 내용을 중심으로 'How to programming'에 대한 정확한 해법을 제시하기 때문에 초보 프로그래머들은 이 책을 보는 것만으로도 실무 감각을 쉽게 익힐 수 있다. 

또 필자가 엄선한 좋은 프로그래밍 습관을 통해 같은 코드를 작성하더라도 전체 소스 코드의 흐름을 파악해서 가장 적합하고 효율적인 명령으로 표현하는 방법을 가르쳐 준다.

 

[ 목 차 ]

01장 코드를 만들기 전 사전 협의는 반드시 필요하다

02장 상태 제어를 잘하는 것이 고수가 되는 지름길이다

03장 뛰어난 목수는 연장을 직접 만들어 사용한다

04장 제대로 사용한 case 문 하나, 열 if 문 안 부럽다

05장 전처리기를 이용하여 다중 파일을 사용한다

06장 칭찬은 개발자를 춤추게 한다

 

특집 프로그래밍에 관한 일반적인 궁금증

 

07장 주석이 없는 코드를 이해할 수 있는 사람은 없다

08장 지뢰밭 코드

09장 C 언어의 객체 지향 개념, static

10장 변수와 자료형은 함께 생각한다

11장 데이터의 자물쇠 기능, const

12장 함수의 다섯 가지 사용 규칙

 

특집 프로그래밍 언어의 종류에 관한 궁금증

 

13장 함수 튜닝의 좋은 습관 세 가지

14장 초보들의 실수 열 가지

15장 연산자를 효과적으로 사용한다

16장 열거형을 사용하는 재미

17장 연산자를 사용할 때 주의할 점

18장 메모리, 재대로 알고 사용하자

 

특집 하드웨어 프로그래밍에 관한 궁금증

 

19장 배열과 포인터의 찰떡궁합

20장 배열보다 더 강력한 포인터의 고급 기능

21장 포인터를 사용할 때 이것만큼은 주의하자

22장 포인터를 이용한 메모리의 할당과 해제

23장 포인터를 이용한 메모리의 복사와 초기화

24장 함수 포인터의 오묘함에 대하여

 

특집 비주얼 C++에 관한 궁금증

 

25장 구조체만 잘 써도 B 학점은 받는다

26장 고수들이 사용하는 구조체 활용 노하우

27장 코드 안에서 메모리를 공유하자

28장 애벌빨래, 전처리기

29장 초보는 모르는 개발 도구, Make

30장 프로그래밍의 마술사, 디버깅

 

Posted by devanix
2011. 9. 10. 07:58


'Essential Tools' 카테고리의 다른 글

넷캣(Netcat) 간단한 사용방법  (2) 2011.12.06
맨페이지 간단한 색상 추가해서 보기  (0) 2011.10.25
[gcc] 옵션 정리  (0) 2011.06.27
DJGPP 설정  (1) 2011.05.08
[Port scanner] Nmap Security Scanner  (0) 2010.09.04
Posted by devanix
2011. 9. 8. 05:50

'Programming > C' 카테고리의 다른 글

printf 출력 서식  (0) 2011.10.12
The C Library Reference Guide  (0) 2011.09.08
비트 제어 - 설정, 클리어, 반전, 검사, 추출  (0) 2011.07.29
다시 체계적으로 배우는 C언어 포인터  (0) 2011.07.07
연산자 우선순위  (0) 2010.05.05
Posted by devanix
2011. 9. 8. 05:37
Posted by devanix
2011. 9. 3. 22:35

재시작 라이브러리(restart library)는 순간적인 사건으로 요청한 작업을 완료하지 못했을 경우

스스로 재시작할 수 있는 함수들의 집합이다.

 

재시작 라이브러리는 시그널에 의한 중단과 불완전한 I/O 두 부분에 중점을 두고 있다.

예를 들어 read, write와 같은 라이브러리 함수들은 I/O 명령을 완전히 실행하기 전에

시그널에 의해 중단될 때 -1을 리턴하고, errno를 EINTR로 설정한다.

 

이러한 중단은 실제로 에러 상황이 아닌 프로그램이 블로킹 I/O를 사용할 때 시그널의 처리를 위한 자연스러운

사건이다. 함수가 -1을 리턴하고, errno가 EINTR로 설정된 경우 재시작 함수는 재시작 과정을 수행한다.

 

[ restart.c ]

/data/workspace/b-example/usp/appendixB/restart.c.html
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <string.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/wait.h>
#include "restart.h"
#define BLKSIZE PIPE_BUF
#define MILLION 1000000L
#define D_MILLION 1000000.0

/* Private functions */

static int gettimeout(struct timeval end,
                               struct timeval *timeoutp) {
   gettimeofday(timeoutp, NULL);
   timeoutp->tv_sec = end.tv_sec - timeoutp->tv_sec;
   timeoutp->tv_usec = end.tv_usec - timeoutp->tv_usec;
   if (timeoutp->tv_usec >= MILLION) {
      timeoutp->tv_sec++;
      timeoutp->tv_usec -= MILLION;
   }
   if (timeoutp->tv_usec < 0) {
      timeoutp->tv_sec--;
      timeoutp->tv_usec += MILLION;
   }
   if ((timeoutp->tv_sec < 0) ||
       ((timeoutp->tv_sec == 0) && (timeoutp->tv_usec == 0))) {
      errno = ETIME;
      return -1;
   }
   return 0;
}

/* Restart versions of traditional functions */

int r_close(int fildes) {
   int retval;
   while (retval = close(fildes), retval == -1 && errno == EINTR) ;
   return retval;
}

int r_dup2(int fildes, int fildes2) {
   int retval;
   while (retval = dup2(fildes, fildes2), retval == -1 && errno == EINTR) ;
   return retval;
}


int r_open2(const char *path, int oflag) {
   int retval;
   while (retval = open(path, oflag), retval == -1 && errno == EINTR) ;
   return retval;
}

int r_open3(const char *path, int oflag, mode_t mode) {
   int retval;
   while (retval = open(path, oflag, mode), retval == -1 && errno == EINTR) ;
   return retval;
}

ssize_t r_read(int fd, void *buf, size_t size) {
   ssize_t retval;
   while (retval = read(fd, buf, size), retval == -1 && errno == EINTR) ;
   return retval;
}

pid_t r_wait(int *stat_loc) {
   pid_t retval;
   while (((retval = wait(stat_loc)) == -1) && (errno == EINTR)) ;
   return retval;
}

pid_t r_waitpid(pid_t pid, int *stat_loc, int options) {
   pid_t retval;
   while (((retval = waitpid(pid, stat_loc, options)) == -1) &&
           (errno == EINTR)) ;
   return retval;
}

ssize_t r_write(int fd, void *buf, size_t size) {
   char *bufp;
   size_t bytestowrite;
   ssize_t byteswritten;
   size_t totalbytes;

   for (bufp = buf, bytestowrite = size, totalbytes = 0;
        bytestowrite > 0;
        bufp += byteswritten, bytestowrite -= byteswritten) {
      byteswritten = write(fd, bufp, bytestowrite);
      if ((byteswritten == -1) && (errno != EINTR))
         return -1;
      if (byteswritten == -1)
         byteswritten = 0;
      totalbytes += byteswritten;
   }
   return totalbytes;
}

/* Utility functions */

struct timeval add2currenttime(double seconds) {
   struct timeval newtime;

   gettimeofday(&newtime, NULL);
   newtime.tv_sec += (int)seconds;
   newtime.tv_usec += (int)((seconds - (int)seconds)*D_MILLION + 0.5);
   if (newtime.tv_usec >= MILLION) {
      newtime.tv_sec++;
      newtime.tv_usec -= MILLION;
   }
   return newtime;
}

int copyfile(int fromfd, int tofd) {
   int bytesread;
   int totalbytes = 0;

   while ((bytesread = readwrite(fromfd, tofd)) > 0)
      totalbytes += bytesread;
   return totalbytes;
}

ssize_t readblock(int fd, void *buf, size_t size) {
   char *bufp;
   ssize_t bytesread;
   size_t bytestoread;
   size_t totalbytes;

   for (bufp = buf, bytestoread = size, totalbytes = 0;
        bytestoread > 0;
        bufp += bytesread, bytestoread -= bytesread) {
      bytesread = read(fd, bufp, bytestoread);
      if ((bytesread == 0) && (totalbytes == 0))
         return 0;
      if (bytesread == 0) {
         errno = EINVAL;
         return -1;
      }
      if ((bytesread) == -1 && (errno != EINTR))
         return -1;
      if (bytesread == -1)
         bytesread = 0;
      totalbytes += bytesread;
   }
   return totalbytes;
}

int readline(int fd, char *buf, int nbytes) {
   int numread = 0;
   int returnval;

   while (numread < nbytes - 1) {
      returnval = read(fd, buf + numread, 1);
      if ((returnval == -1) && (errno == EINTR))
         continue;
      if ((returnval == 0) && (numread == 0))
         return 0;
      if (returnval == 0)
         break;
      if (returnval == -1)
         return -1;
      numread++;
      if (buf[numread-1] == '\n') {
         buf[numread] = '\0';
         return numread;
      }
   }
   errno = EINVAL;
   return -1;
}

ssize_t readtimed(int fd, void *buf, size_t nbyte, double seconds) {
   struct timeval timedone;

   timedone = add2currenttime(seconds);
   if (waitfdtimed(fd, timedone) == -1)
      return (ssize_t)(-1);
   return r_read(fd, buf, nbyte);
}

int readwrite(int fromfd, int tofd) {
   char buf[BLKSIZE];
   int bytesread;

   if ((bytesread = r_read(fromfd, buf, BLKSIZE)) < 0)
      return -1;
   if (bytesread == 0)
      return 0;
   if (r_write(tofd, buf, bytesread) < 0)
      return -1;
   return bytesread;
}

int readwriteblock(int fromfd, int tofd, char *buf, int size) {
   int bytesread;

   bytesread = readblock(fromfd, buf, size);
   if (bytesread != size)         /* can only be 0 or -1 */
      return bytesread;
   return r_write(tofd, buf, size);
}

int waitfdtimed(int fd, struct timeval end) {
   fd_set readset;
   int retval;
   struct timeval timeout;

   if ((fd < 0) || (fd >= FD_SETSIZE)) {
      errno = EINVAL;
      return -1;
   }
   FD_ZERO(&readset);
   FD_SET(fd, &readset);
   if (gettimeout(end, &timeout) == -1)
      return -1;
   while (((retval = select(fd+1, &readset, NULL, NULL, &timeout)) == -1)
           && (errno == EINTR)) {
      if (gettimeout(end, &timeout) == -1)
         return -1;
      FD_ZERO(&readset);
      FD_SET(fd, &readset);
   }
   if (retval == 0) {
      errno = ETIME;
      return -1;
   }
   if (retval == -1)
      return -1;
   return 0;
}

 

[ restart.h ]

/data/workspace/b-example/usp/appendixB/restart.h.html
#ifndef __RESTART_H_
#define __RESTART_H_

#include <fcntl.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>

#ifndef ETIME
#define ETIME ETIMEDOUT
#endif

struct timeval add2currenttime(double seconds);
int copyfile(int fromfd, int tofd);
int r_close(int fildes);
int r_dup2(int fildes, int fildes2);
int r_open2(const char *path, int oflag);
int r_open3(const char *path, int oflag, mode_t mode);
ssize_t r_read(int fd, void *buf, size_t size);
pid_t r_wait(int *stat_loc);
pid_t r_waitpid(pid_t pid, int *stat_loc, int options);
ssize_t r_write(int fd, void *buf, size_t size);
ssize_t readblock(int fd, void *buf, size_t size);
int readline(int fd, char *buf, int nbytes);
ssize_t readtimed(int fd, void *buf, size_t nbyte, double seconds);
int readwrite(int fromfd, int tofd);
int readwriteblock(int fromfd, int tofd, char *buf, int size);
int waitfdtimed(int fd, struct timeval end);

#endif /* __RESTART_H_ */

 

Posted by devanix
2011. 9. 3. 02:32

Catexit 함수는 사용자가 정의한 종료 핸들러를 등록한다.

종료 핸들러는 프로그램이 main 함수로부터 리턴 하거나 exit 함수를 호출하였을 때

마지막에 등록된 핸들러가 먼저 실행되는 순서로 실행. (여러 번 호출하여 핸들러 등록 가능)

 

 

showtimes 예제는 showtimes 함수를 exit 핸들러로 등록하여

프로그램과 그 자식들이 사용한 시간에 대한 통계를 표준 에러로 출력한다.

 

[ 예제 코드 : showtimes.c ]

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/times.h>
#include <sys/wait.h>

int wastetime(int maxus);

static void showtimes(void) {
   double ticks;
   struct tms tinfo;

   if ((ticks = (double) sysconf(_SC_CLK_TCK)) == -1)
      perror("Failed to determine clock ticks per second");
   else if (times(&tinfo) == (clock_t)-1)
      perror("Failed to get times information");
   else {
      fprintf(stderr, "User time:              %8.3f seconds\n",
         tinfo.tms_utime/ticks);
      fprintf(stderr, "System time:            %8.3f seconds\n",
         tinfo.tms_stime/ticks);
      fprintf(stderr, "Children's user time:   %8.3f seconds\n",
         tinfo.tms_cutime/ticks);
      fprintf(stderr, "Children's system time: %8.3f seconds\n",
         tinfo.tms_cstime/ticks);
   }
}
 
int main(void) {
   if (atexit(showtimes))  {
      fprintf(stderr, "Failed to install showtimes exit handler\n");
      return 1; 
   }
   if (fork())
      wastetime(2900000);
   else
      wastetime(5400000);
   if (wait(NULL) > 0)
      fprintf(stderr, "\nChild has exited, parent stats follow:\n");
   return 0;
}

times 함수는 시간 틱(tick) 수의 시간 정보를 나타낸다.

 

 

[ 예제 코드 : wastetime.c ]

#include <stdio.h>
#include <sys/time.h>
#define MILLION 1000000L

/* waste maxus microseconds of time */
int wastetime(int maxus) {
   long timedif;
   struct timeval tp1, tp2;

   if (gettimeofday(&tp1, NULL)) {
      fprintf(stderr, "Failed to get initial time\n");
      return 1;
   }
   timedif = 0;
   while (timedif < maxus) {
      if (gettimeofday(&tp2, NULL)) {
         fprintf(stderr, "Failed to get check time\n");
         return 1;
      }
      timedif = MILLION*(tp2.tv_sec - tp1.tv_sec) +
                   tp2.tv_usec - tp1.tv_usec;
      if (timedif < 0)
         break;
   }
   return 0;
}

 

▶ 사용 예:

$ ./showtimes

User time:                         1.270 seconds

System time:                    2.660 seconds

Children's user time:      0.000 seconds

Children's system time: 0.000 seconds

 

Child has exited, parent stats follow:

User time:                        0.460 seconds

System time:                    0.970 seconds

Children's user time:      1.270 seconds

Children's system time: 2.660 seconds

 

Posted by devanix