Android Support Annotations 라이브러리를 활용한 결함 탐지방법

1. Annotations 이란 ?

 - Annotation 은 Java 코드에서 추가할 수 있는 메타데이터로, @ 기호로 시작한다.

 - 상위 클래스의 메서드를 오버라이드 한다는 정보를 표현하는 @Override 애너테이션이 대표적이다.

 - 추가 정보를 문법적으로 표현할 수 있기 때문에 Java 에서는 API의 의도를 애너테이션으로 명시하고 결함 탐지에 활용하는 기법이 발달 했다.

 

2. Java 에서 제공되는 Annotations

 - @Override : 함수 Override 일 경우에 사용되는 Annotations

 - @Deprecated : 해당 변수, 함수 명이 삭제될 수 있음을 나타낼 때 사용

 - @SuppressWarnings : 권장하지 않는 구문에 대하여 노랑색으로 경고를 표시해 주는 경우

 

3. Android Support Library 의 Annotations

build.gradle 에 아래의 코드를 추가하면 사용 가능하다.

dependencies {

   compile 'com.android.support:support-annotations:20.0.0'

}

 

3.1 @StringRes, @DrawableRes, @ColorRes

위 세가지의 Annotations 는 리소스 아이디 관련 애너테이션이다. 요소의 값이 이 애너테이션들이 의미하는 리소스 타입에 해당하는 리소스 아이디임을 의미한다.

Android 의 모든 리소스는 R 클래스에 의해 int 타입의 아이디로 관리된다. 그래서 drawable 타입 리소스의 아이디를 넣어야 할 곳에 문자열 리소스의 아이디나 정수 값을 넣는 등 실수를 범할 수 있는데, 이 애너테이션을 이용하면 실수를 방지할 수 있다.

void setMessage(@StringRes int resId) {  
    mMessage = mContext.getText(resId);
}

// ...

setMessage(R.string.error_retry);    // OK

int stringId = R.string.error_retry;  
setMessage(stringId);                // OK

setMessage(R.color.white);           // ERROR: Expected resource of type string  
setMessage(1);                       // ERROR: Expected resource of type string 

 

3.2 @ColorInt

@ColorInt 이너테이션은 해당 값이 0xff99ff99와 같은 ARGB 컬러 정수임을 나타낸다. 글자가 비슷한 @ColorRes 애너테이션은 R.color.divider 와 같은 Color Drawable 타입 리소스의 아이디를 나타내므로 혼동하지 않도록 주의한다.

다음 코드는 int 타입 파라미터의 값을 @ColorInt 애너테이션과 @ColorRes 애너테이션으로 명확하게 구분한 예이다.

void setColor(@ColorInt int color) {  
    mColor = color;
}

void setColorRes(@ColorRes int resId) {  
    setColor(mContext.getResources().getColor(resId));
}

// ...

setColor(0xff99ff99);         // OK  
setColor(R.color.divider);    // ERROR: Should pass resolved color instead of resource id  
                              //        here: `getResources().getColor(R.color.divider)`

setColorRes(0xff99ff99);      // ERROR: Expected resource of type color  
setColorRes(R.color.divider); // OK  

 

3.3 @IntRange, @FloatRange

@IntRange 애너테이션 @FloatRange 애너테이션은 숫자형 값의 범위를 한정한다. from 파라미터와 to 파라미터를 지니며, 값의 범위는 from 파라미터의 값부터 to 파라미터의 값 사이다. 투명도에서 0~1 범위의 소숫값(decimal)을 사용하는지, 색상 표현에서 0~255 범위의 정숫값을 사용하는지 혼동하기 쉬운데, 이런 경우에 유용하게 쓰일 수 있다. @IntRange 애너테이션은 int 타입뿐만 아니라 long 타입에도 사용할 수 있다. 마찬가지로 @FloatRange 애너테이션은 float 타입뿐만 아니라 double 타입에도 사용할 수 있다.

다음 코드는 @FloatRange 애너테이션으로 alpha 파라미터의 값이 0.0~1.0이어야 함을 나타내는 예다.

void setAlpha(@FloatRange(from=0.0, to=1.0) float alpha) {  
    // if (alpha < 0 || alpha > 1.0f) {
    // throw new IllegalArgumentException();
    // }
}

// ...

setAlpha(0.5f); // OK  
setAlpha(127);  // ERROR: Value must be ≥ 0.0 and ≤ 1.0 (was 127)  

 

3.4 @UiThread, @WorkerThrea

@UiThread 애너테이션 @WorkerThread 애너테이션, 은 스레드 관련 애너테이션이다. 이 애너테이션들이 의미하는 스레드와 동일한 유형의 스레드에서만 해당 메서드를 호출할 수 있도록 제약한다.

@WorkerThread
void backgroundJob() {  
}

@UiThread
void uiJob() {  
}

// ...

@WorkerThread
void backgroundJob2() {  
    backgroundJob();                  // OK
    uiJob();                          // ERROR: Method uiJob must be called from the UI
                                      // thread, currently inferred thread is worker
    view.setVisibility(View.VISIBLE); // ERROR: Method setVisibility must be called from
                                      // the UI thread, currently inferred thread is
                                      // worker
}

@UiThread
void uiJob2() {  
    backgroundJob();                  // ERROR: Method backgroundJob must be called from
                                      // the worker thread, currently inferred thread is
                                      // UI
    uiJob();                          // OK
    view.setVisibility(View.VISIBLE); // OK
}

 

3.5 @CallSuper

@CallSuper 애너테이션은 이 애너테이션이 붙은 메서드를 하위 클래스에서 오버라이드할 때는 반드시 상위 클래스의 메서드를 호출하도록 강제한다. 액티비티의 라이프사이클 메서드에도 사용된다.

다음 코드는 foo() 메서드에 @CallSuper 애너테이션을 붙여 foo() 메서드를 오버라이드한 하위 클래스에서 반드시 super.foo() 메서드를 호출하도록 강제하는 예다.

 

class Super {  
    @CallSuper
    void foo() {
    }

    void bar() {
    }
}

// ...

class Example1 extends Super {  
    @Override
    void foo() {                // ERROR: Overriding method should call 'super.foo'
    }

    @Override
    void bar() {                // OK
    }
}

class Example2 extends Super {  
    @Override
    void foo() {                // OK
        super.foo();
    }

    @Override
    void bar() {                // OK
        super.bar();
    }
}

class ExampleActivity extends Activity {  
    protected void onCreate(Bundle saved) { // ERROR: Overriding method should call
                                            //        'super.onCreate'
    }
}

 

3.6 @NonNull, @Nullable

 

@NonNull 애너테이션 @Nullable 애너테이션은 null 값 처리에 관련된 애너테이션이다.

@NonNull 애너테이션은 값이 'null'이 아니라는 것을 나타낸다. 예를 들어 @NonNull 애너테이션이 붙은 변수에 null 값을 대입하면 경고가 나타난다.

@Nullable 애너테이션은 값이 'null'일 수 있다는 것을 나타낸다. 예를 들어 @Nullable 애너테이션이 붙은 변수를 null 검사 없이 사용하면 경고가 나타난다.

다음 코드는 필드, 파라미터, 메서드에 @NonNull 애너테이션과 @Nullable 애너테이션을 사용한 예다.

 

class Example {  
    @NonNull
    final Context mContext;
    @Nullable
    View mView;

    Example(@NonNull Context context) {
        // context 파라미터에 @NonNull 애너테이션이 붙어 있으므로
        // 파라미터의 값이 null이 아닐 것이라 가정한다.
        // 그래서 null 검사가 필요 없다.
        // if (context == null) {
        // throw new NullPointerException("context");
        // }
        mContext = context;
    }

    @NonNull
    Context getContext() {
        return mContext;
    }

    void setView(@Nullable View view) {
        mView = view;
    }

    @Nullable
    View getView() {
        return mView;
    }
}

// ...

Context context = null;  
new Example(context);        // WARNING: Argument 'context' might be null

new Example(null);           // WARNING: Passing 'null' argument to parameter annotated as @NonNull

new Example(nonNullContext); // OK

View view = getView();  
view.getTag();               // WARNING: Method invocation 'view.getTag()' may produce  
                             // 'java.lang.NullPointerException'

@NonNull View mView;
mView = getView();           // WARNING: Expression 'getView()' might evaluate to null but  
                             // is assigned to a variable that is annotated with @NonNull

 

http://d2.naver.com/helloworld/8725603

http://thdev.net/616

'Android' 카테고리의 다른 글

[AndroidStudio] git clone 방법  (0) 2023.05.27

Android PowerManager.WakeLock

 

일반적으로 Android 는 애플리케이션이나 서비스에 전력이 필요하지 않으면 CPU도 전력을 소모하지 않게 설계되어 있습니다.

따라서 화면이 꺼진 뒤 일정 시간이 지나면 CPU 가 딥 슬립(deep sleep) 상태로 전환됩니다.

딥 슬립 상태로 전환되면 백그라운드에서 동작하는 서비스도 CPU를 사용하지 못하기 때문에, 아래와 같은 일이 발생할 수 있습니다.

1. BroadcastReceiver 클래스의 onReceive() 메서드가 호출 되었지만, 관련 로직을 수행하지 않음.

2. AlarmManager 클래스를 사용하여 트리거가 발생 하였지만, CPU 가 실행상태가 아니므로 이후 로직을 사용하지 않음.

 

이를 방지하기 위해 Android 에서는 PowerManager.WakeLock 클래스를 제공하여 CPU가 딥 슬립상태에서 실행 상태로 전환되어 유지될 수 있도록 합니다. 다만, Device 의 베터리 수명은 이 API 를 어떻게 사용하느냐에 따라 크게 달라지기 때문에, PowerManager.WakeLock 은 정말 필요한 경우가 아니면 사용하지 말아야 하고, 만약 사용 하더라도 가능한 최소 수준을 사용하고, 최대한 빨리 release 해주어야 합니다.

WakeLock 을 사용하기 위한 API 는 newWakeLock() 입니다. 이 함수는 PowerManager.WakeLock object 를 생성 합니다. 이 객체를 사용하여 Device 의 Power 를 제어할 수 있습니다.

Application 에서 이를 사용하는 방법은 아주 간단 합니다.

PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
 PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
 wl.acquire();
   ..screen will stay on during this section..
 wl.release();

newWakeLock 에 사용되는 Flag 의 종류는 아래와 같이 4가지가 있습니다.

PARTIAL_WAKE_LOCK 을 사용하면 스크린 상태에 관계 없이, 사용자가 전원 버튼을 눌러 단말을 sleep 시켜도 계속 CPU는 실행상태에 있습니다.

다른 PARTIAL_WAKE_LOCK 을 제외한 다른 Flag 를 사용하면 사용자가 전원버튼을 눌렀을 때 CPU는 sleep 상태로 들어갈 수 있습니다.

 

WakeLock 클래스의 로직은 IOS 미만 버전과 이상 버전의 로직이 다릅니다.

IOS 이전에는 WakeLock.acquire(timeout) 메서드에 치명적인 문제점이 존재하는데, 이 메서드는 timeout 시간만큼 CPU 를 사용하고 나면 자동으로 WakeLock.release() 메서드가 호출되어 CPU 사용을 마칩니다. 그런데 timeout 시간보다 먼저 작업이 끝나서 명시적으로 WakeLock.release() 메서드를 호출 하더라도 timeout 시간이 지나면 다시한번 WakeLock.release() 메서드가 호출되는데, 그러면 이미 CPU 사용을 마쳤는데도 다시한번 CPU 사용을 마치려 시도하면서 충돌이 발생합니다.

 

FATAL EXCEPTION: main  
java.lang.RuntimeException: WakeLock under-locked  
    at android.os.PowerManager$WakeLock.release(PowerManager.java:307)
    at android.os.PowerManager$WakeLock.release(PowerManager.java:282)
    at android.os.PowerManager$WakeLock$1.run(PowerManager.java:202)
    at android.os.Handler.handleCallback(Handler.java:587)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:130)
    at android.app.ActivityThread.main(ActivityThread.java:3691)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:507)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:847)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:605)
    at dalvik.system.NativeStart.main(Native Method)

충돌이 발생하는 원인을 살펴보기 위해 WakeLock.acquire(timeout) 메서드의 코드를 살펴보면, 다음과 같이 postDelayed() 메서드를 사용하여 timeout 시간 뒤에 mReleaser 객체가 호출되게 됩니다. mReleaser 객체는 Runnable 객체로, WakeLock.release() 메서드를 호출합니다.

/**
 * Makes sure the device is on at the level you asked when you created
 * the wake lock. The lock will be released after the given timeout.
 * 
 * @param timeout Release the lock after the give timeout in milliseconds.
 */
public void acquire(long timeout) {
    synchronized (mToken) {
        acquireLocked();
        mHandler.postDelayed(mReleaser, timeout);
    }
}
private final Runnable mReleaser = new Runnable() { 1254 public void run() { 1255 release(); 1256 } 1257 };

왜 postDelated() 메서드에 의해서 충돌이 발생하는지 ICS 미만 버전과 ICS 이상 버전에 있는 PowerManager.java 의 소스코드를 살펴보면 알 수 있습니다.

ICS 미만 버전의 PowerManager.java 에서 release() 메서드의 코드는 다음과 같습니다.

public void release(int flags)  
{
    synchronized (mToken) {
        if (!mRefCounted || --mCount == 0) {
            try {
                mService.releaseWakeLock(mToken, flags);
            } catch (RemoteException e) {
            }
            mHeld = false;
        }
        if (mCount < 0) {
            throw new RuntimeException("WakeLock under-locked " + mTag);
        }
    }
}

 

위 두 소스 코드를 비교해 보면 ICS 이상 버전에 removeCallbacks(mRelease) 메서드가 추가된 것을 볼 수 있습니다 .

이처럼 timeout 시간보다 먼저 release() 메서드가 호출되었을 때에는 timeout 시간이 지난 뒤 다시한번 release() 메서드가 호출되지 않도록 하여 충돌이 발생하지 않습니다.

Handler.removeCallbacks() 메서드는 postDelayed() 메서드를 통해 메시지 큐에 등록된 Runnable 객체를 삭제합니다.

 

WakeLock  효율적으로 사용하기

WakeLock 은 배터리에 가장 영향을 많이 끼치는 기능 중 하나이므로, 사용할 수 밖에 없는 상황이라면 최대한 효율적으로 사용해야 합니다.

WakeLock 을 효율적으로 사용하는 방법은 다음과 같습니다.

 

1. 어떤 동작을 수행하려고 WakeLock 을 사용했다면 동작이 종료되었을 때 바로 release() 메서드를 호출한다.

2. 연산이 끝나면 항상 release() 메서드가 호출되고 Exception 등으로 인해 release() 메서드가 호출되지 못하는 일이 없도록 finally 키워드를 사용한다.

3. 패킷 발신, 수신에 사용한다면 발신할 때 한번 수신할때 다시한번 WakeLock을 호출 하여 두번의 호출이 생긴다. 만약 발신 후 수신까지 시간이 매우 짧다면 WakeLock.acquire(timeout) 메서드를 사용하여 호출 횟수를 줄일 수 있다.

4. 화면이 켜진 상태라면 CPU는 딥슬립 모드로 전환되지 않기 때문에 화면이 꺼진 상태에서만 WakeLock 을 사용하고, 화면이 켜진 상태에서는 WakeLock 을 사용하지 않는 것도 좋은 방법이다.

5. BroadcastReceiver 클래스를 사용하는 경우에도 onReceive(...) 메서드 이후의 로직이 언제나 정상적으로 수행되게 하기 위해서 WakeLock 을 사용한다. 하지만 Android Support Library 를 사용하면 WakefulBroadcastReceiver 클래스를 사용하여 직접 WakeLock 을 호출하지 않아도 된다.

 

 

WakefullBroadcastReceiver 클래스는 startWakefulService() 메서드로 서비스 (주로 IntentService) 에 처리해야 할 작업을 전달 해주고, 이러한 전달 과정에서 단말이 딥 슬립 상태로 돌아가지 않도록 내부적으로 PARTIAL_WAKE_LOCK 을 획득 합니다. 서비스에서 작업이 완료 되면 completeWakefullIntent() 메서드를 호출하여 WakeLock 사용을 종료하게 됩니다.

 

 

기존에는 그냥 연습용 앱이어서 해상도 별로 이미지를 생각하지 않고 drawable 에 다 때려 넣었다.

그러다보니 여러명에서 같이 하는 앱개발의 경우에, 각기 다른 Android Device 를 가지고 있어서, OutOfMemory  error 가 나는 경우가 많았다.

그래서 찾아봤는데 아래 사이트에서 각 해상도별 이미지를 생성 해준다.

https://romannurik.github.io/AndroidAssetStudio/nine-patches.html#&sourceDensity=320&name=example

 

Android Asset Studio - Simple nine-patch generator

Drag or select a source graphic to get started.

romannurik.github.io

 

생성된 이미지를 아래와 같이 적용해주기만 하면 된다.

splash_logo.png

 

그리고 불러다 쓸때는 기존에 @drawble/splash_logo 가 아닌 @mipmap/splash_logo 라고 해주면, 알아서 돌아가는 device 에 맞게 이미지를 로드 해준다. 

<ImageView
    android:id="@+id/splash_img"
    android:layout_width="0dp"
    android:layout_height="290dp"
    android:scaleType="fitCenter"
    android:contentDescription="@string/contentDescription_img"
    app:layout_constraintWidth_percent="0.7"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.276"
    app:srcCompat="@mipmap/splash_logo" />

 

굿굿 

1. File > New > Project from Version Control... 선택

 

2. Clone 하고자 하는 repository 의  URL 입력

'Android' 카테고리의 다른 글

Anotation  (0) 2024.11.10

waitpid 함수는 wait 함수처럼 자식 프로세스를 기다릴때 사용하는 함수입니다. 즉, 자식 프로세스의 종료상태를 회수할 때 사용합니다.

하지만 waitpid 함수는 자식 프로세스가 종료될 때 까지 차단되는 것을 원하지 않을 경우, 옵션을 사용하여 차단을 방지할 수 있습니다.

그리고 기다릴 자식 프로세스를 좀더 상세히 지정할 수 있습니다.


#include <sys/wait.h>

 pid_t waitpid(pid_t pid, int *statloc , int options);

 성공 : 프로세스 ID 반환

 오류 : -1 



waitpid 함수의 첫번째 인자에 대해서 알아 보겠습니다.

 wiatpid 함수의 첫 번째 인자

 의미

  pid가 -1 일 경우  (pid ==  -1)

   임의의 자식 프로세스를 기다림

  pid가 0 보다 클 경우 (pid > 0)

   프로세스 ID가 pid인 자식 프로세스를 기다림

  pid가 -1 보다 작을 경우 (pid < -1)

  프로세스 그룹 ID가 pid의 절댓값과 같은 자식 프로세스를 기다림

 pid가 0일 경우 (pid == 0)

 waitpid를 호출한 프로세스의 프로세스 그룹 PID와 같은 프로세스 그룹 ID를 가진 프로세스를 기다림 

waitpid 함수의 오류(-1)는 지정한 pid의 프로세스 또는 프로세스 그룹이 없는 경우에 발생하며 그리고 pid가 자식 프로세스가 아닐 때 발생합니다.


두번째 인자에 대해서 알아 보겠습니다.

 

 waitpid 함수 반환 값

 두 번째 인자 인 statloc 값

 자식 프로세스가 정상적으로 종료

 프로세스 ID

 - WIFEXITED(statloc) 매크로가 true를 반환

 - 하위 8비트를 참조하여 자식 프로세스가 exit, _exit, _Exit에 넘겨준 인자값을 얻을 수 있음, WEXITSTATUS(statloc)

  자식 프로세스가 비정상적으로 종료

 프로세스 ID

 - WIFSIGNALED(statloc) 매크로가 true를 반환

 - 비정상 종료 이유를 WTERMSIG(statloc) 매크로를 사용하여 구할 수 있음

 waitpid 함수 오류  -1

 - ECHILD : 호출자의 자식 프로세스가 없는 경우

 - EINTR : 시스템 콜이 인터럽트 되었을 때


세번째 인자에 대해서 알아 보겠습니다.

 세 번째 인자로 사용가능한 상수

 의미 

 WCONTINUED

 중단 되었다가 재개된 자식 프로세스의 상태를 받음

 WNOHANG 

 기다리는 PID가 종료되지 않아서 즉시 종료 상태를 회수 할 수 없는 상황에서 호출자는 차단되지 않고 반환값으로 0을 받음

 WUNTRACED 

 중단된 자식 프로세스의 상태를 받음


아래 예제는 세 번째 인자에 아무것도 주지 않았을 때 wait 함수와 동일한 동작하는 것을 확인해 보겠습니다.

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int main() { pid_t childPid; int status; childPid = fork(); if(childPid > 0) { // 부모 프로세스 int ret; printf("부모 PID : %ld, pid : %d\n",(long)getpid(), childPid); sleep(3); ret = waitpid(childPid,&status,0); // 세번째 인자에 0을 넣었으므로 wait 함수와 동일한 동작을 함 printf("부모 종료 %d %d %d\n",ret,WIFEXITED(status),WEXITSTATUS(status)); exit(0); } else if(childPid == 0){ // 자식 코드 printf("자식 시작 PID : %ld\n", (long)getpid()); sleep(8); printf("자식 종료\n"); exit(0); } else { // fork 실패 perror("fork Fail! \n"); return -1; } return 0; }


실행 결과를 보겠습니다.

부모 PID : 13444, pid : 13445

자식 시작 PID : 13445

자식 종료

부모 종료 13445 1 0 

부모 프로세스는 3초 동안 동작하고 자식 프로세스는 8초 동안 동작하는 예제 코드입니다. 하지만 결과는 자식 프로세스가 먼저 종료되고 부모 프로세스가 종료되었습니다.

waitpid 함수는 세번째 인자로 0을 주었기 때문에 자식 프로세스가 종료될 때 까지 blocking 되었기 때문입니다.

WIFEXITED 매크로는 1(true)를 리턴하였으며 WEXITSTATUS는 자식 프로세스가 0을 리턴 한 것을 알려주고 있습니다.


다음 예제는 세 번째 인자에 WNOHANG 옵션을 넣어보도록 하겠습니다.

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int main() { pid_t childPid; int status; childPid = fork(); if(childPid > 0) { // 부모 프로세스 int ret; printf("부모 PID : %ld, pid : %d\n",(long)getpid(), childPid); sleep(3); ret = waitpid(childPid,&status,WNOHANG); // WNOHANG 옵션을 사용하면 자식 프로세스가 종료되지 않았을 경우 blocking 되지 않고 즉시 0값을 리턴합니다. printf("부모 종료 %d %d\n",ret,WIFEXITED(status)); exit(0); } else if(childPid == 0){ // 자식 코드 printf("자식 시작 PID : %ld\n", (long)getpid()); sleep(8); printf("자식 종료\n"); exit(0); } else { // fork 실패 perror("fork Fail! \n"); return -1; } return 0; }

실행 결과를 확인해 보겠습니다.

부모 PID : 14289, pid : 14290

자식 시작 PID : 14290

부모 종료 0 0 127

자식 종료 

waitpid 함수는 0을 리턴하였으며 WIFEXITED 매크로도 false를 리턴하였습니다. WNOHANG 옵션을 사용하였기 때문에 자식 프로세스가 종료될 때 까지 기다리지 않고 waitpid 함수가 바로 0값을 리턴하였기 때문입니다.


마지막으로 존재하지 않는 자식 프로세스의 PID를 waitpid 첫 번째 인자로 넣어 보겠습니다.

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> #include <sys/error.h> #include <sys/string.h> int main() { pid_t childPid; int status; childPid = fork(); if(childPid > 0) { // 부모 프로세스 int ret; printf("부모 PID : %ld, pid : %d\n",(long)getpid(), childPid); sleep(3); ret = waitpid(99999,&status,WNOHANG); // pid로 99999(존재하지 않는 프로세스 ID) printf("부모 종료 %d %d %s\n",ret,errno,strerror(errno)); // 오류가 발생하였을 경우 errno값을 확인 exit(0); } else if(childPid == 0){ // 자식 코드 printf("자식 시작 PID : %ld\n", (long)getpid()); sleep(8); printf("자식 종료\n"); exit(0); } else { // fork 실패 perror("fork Fail! \n"); return -1; } return 0; }

실행 결과를 보도록 하겠습니다.

부모 PID : 17859, pid : 17860

자식 시작 PID : 17860

부모 종료 -1 10 No child processes

자식 종료 

waitpid 함수는 -1 오류 값을 반환하였습니다. 그리고 에러 넘버(errno) 10, 즉 NO child processes라는 에러를 출력하였습니다. 

99999라는 자식 프로세스가 존재하지 않는다는 의미입니다. 그 이유는 waitpid 함수의 첫 번째 인자로 존재하지 않는 99999 PID값을 입력하였기 때문입니다.


지금까지 wait 함수보다 좀더 다양한 기능을 제공하는 waitpid 함수에 대해서 알아 보았습니다.

+ Recent posts