Rough Block Diagram

[Display Device] <>--- [Logical Display] <>--- [WindowManager]

DisplayInfo

has followings (Initial values are set as information from display device).

:logicalHeight

:logicalWidth

:appWidth

:appHeight


DisplayDevice

has followings

:mCurrentLayerStackRect

:mCurrentDisplayRect


shown on the display.

|

v

[LayerStackRect] ---(project to)---> [DisplayRect]

^

|

This value is outbound of WindowManager.


Above information is very rough and... NOT strictly verified.


Here is useful tool supported by Google to handle displaying area and density - wm.

You can find it at '/system/bin/wm'


root@generic:/system/bin # ./wm reset usage: wm [subcommand] [options] wm size [reset|WxH] wm density [reset|DENSITY] wm overscan [reset|LEFT,TOP,RIGHT,BOTTOM]

Very useful isn't it!



'Segmentation fault' is very familiar error for C developer.

Then, when this runtime error is issued?

At the moment of accessing not-allocated-memory?

This may be right, but this is NOT strict enough.

'Segmentation fault' is issued by kernel through signal to notify that,

"You - user process - try to access not-permitted memory area. And usually, this happens when it tries to access not-allocated-memory - for the user process (In case of ARM architecture, it is triggered by Data Abort cpu exception).


Let's see below code.

#include <stdio.h>
#include <stdlib.h>

int
main(int argc, const char *argv[]) {
        int i = 0;
        char *s = malloc(1);
        s += 1;
        for (i = 0; i < 1024 * 1024 * 1024; i++) {
                printf("%d\n", i);
                s[i] = 4;
        }
        return 0;
}

Do you think when this process is stopped by 'Segmentation fault' signal?

"i == 0" ? (that is at the first moment of trying to access not-allocated-memory-by-malloc).


It, is totally up to malloc implementation.

But, at above case, this is definitely NO.

The reason is, kernel(linux) allocate memory for user process at least 1-page.

So, above code will run even if i > 1024 especially on the Linux machine.


And, there are various implementation(algorithm) of malloc.

So, we cannot expect exact value of i for above code.

Important thing to know here is, answer the question - "What exactly does 'Segmentation fault' mean?"


'Language > C&C++' 카테고리의 다른 글

[C/C++] sizeof array  (0) 2015.03.19
[Macro] '##' macro operator  (0) 2015.02.11
[GCC] Initialize array...  (0) 2014.07.25
[Linux] Using named pipe in linux (주의할 점)  (0) 2014.01.09
[c/c++] file copy using 'sendfile' on linux.  (0) 2013.12.17

See https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Designated-Inits.html


And here is one more tips.

Sometimes, you may want to initialize all array element with same value.

Then, you can do it as follows if you are using GCC.


static const int arr[10] = {[0 ... (sizeof(arr)/sizeof(arr[0]) - 1)] = 10};



Android VM은 일반적인 Java VM과 마찬가지로 boot class path와 class path를 지원한다.

그렇지만, Android App(APK)에서 위의 내용들이 어떻게 사용되고 있는가에 대한 점은 상세히 분석해 볼 필요가 있다.

왜냐하면, Android에서는 App(APK)가 처음부터 loading되는 것이 아니고 zygote를 통해서 fork되는 방식이기 때문에  Java의 경우와는 약간 다르기 때문이다.


Class Loader 의 종류와 내용.


VMClassLoader / BootClassLoader

BOOTCLASSPATH만을 load하는 기본적인 class loader


System Class Loader

BootClassLoader + "CLASSPATH" 를 가지는 Loader.

VM이 최초 "static main"을 invoke시킬때 사용하는 class loader이다.

근거:

startVm() -> DvmStartup() -> dvmPrepMainThread()

: SystemClassLoader를 Thread의 ContextClassLoader로 setting함.

AndroidRuntime::start()pEnv->FindClass() - Function pointer to 'dalvik/vm/Jni.cpp:FindClass' - 를 이용해서 "static main"을 invoke하는데, 이때 SystemClassLoader가 사용됨.


Context Class Loader

SystemClassLoader + "Package private configuration"을 가지는 loader.

VM이 뜬 이후, Android context instance - ex. Activity - 를 시작할때 - ex. performLaunchActivity() - 사용되는 class loader로 기본 System Class Loader에 package private configuration(LoadedApk.java) - ex. shared java library - 을 포함한다.

근거:

LoadedApk.getClassLoader() 함수를 자세히 살펴볼 필요가 있는데, package정보의 java shared library나 native shared library정보 등을 이용해서 PathClassLoader를 생성하고 이를 이용하는데, 이때 base class loader가 default로, SystemClassLoader이기 때문이다.


여기서 또 한가지 중요한 내용은, Java의 경우, class가 최초 reference될때, referer가 load될때 사용된 classLoader를 이용해서, reference되는 class도 load된다는 것이다. 이것은 Android VM의 경우도 마찬가지인데 예로, dalvik의 OP_NEW_INSTANCE op code처리 부분을 보면, dvmResolveClass() 를 call하고, 함수 내부의 구현을 보면, referer의 class loader 를 이용하는 것을 볼 수 있다.


이제 정리할 시간이다.

Android의 경우, 최초 vm이 뜰때는 System Class Loader가 사용되나, context가 수행될 때는 Context Class Loader가 사용된다. 따라서, Package(APK)에 추가적으로 사용할 library를 기록해 두면, Context내부에서는 정상적으로 사용이 가능하다.


그러면, Package 에서 사용할 library들은 어떤식으로 정의할 수 있을까?


먼저 사용할 library가 permission file에 정의되어 있고, platform build시 /system/etc/permissions 에 위치해야 한다.

(당연히, 해당 library도 정해진 위치에 있어야 한다.)


com.my.android.test.xml

<permissions>

<library name="com.my.android.test"

         file="/system/framework/com.my.android.test.jar" />

</permissions>


이제 위의 library는 package manager에 의해 shared library로 인식된다.


Package에서 사용하기 위해서는, package의 AndroidManifest.xml에 application element의 sub element로 아래 'uses-library' element를 추가해야 한다.


AndroidManifest.xml

<uses-library android:name="com.my.android.test" />


이렇게 정의되면, package manager service가 Context Class Loader에 해당 library를 추가해 준다.


또 한가지 짚고 넘어가야 할 것은, CLASSPATH 에 대한 부분이다.

CLASSPATH의 경우, VM이 시작할때, setting되는 java.class.path property를 읽는데, 이 값은 Dvm의 classPathStr 값이다.


[[ TO DO ]]

그런데, 재미있는 사실은, system의 CLASSPATH 환경변수를 설정하더라도, Zygote에서 fork되는 App에서는 이를 바로 사용하지 못하는데 (실험적. 확인 필요)이 부분은 좀더 분석해 봐야 한다.



====== Rough Reference - Starting new Activity ======


ActivityStackSupervisor
startActivityLocked
startActivityUncheckedLocked

ActivityStack (target stack)
startActivityLocked

ActivityManagerService

attachApplicationLocked

ActivityStackSupervisor

resumeTopActivityLocked

ActivityManagerService.java
startProcessLocked() -> Process.start("android.app.ActivityThread", app.processName, uid, uid, gids ....)

Process.java
Process.start()
startViaZygote()
zygoteSendArgsAndGetResult() === send argument list via socket ===> zygote

<<< Socket connection >>>

ZygoteInit
registerZygoteSocket()
acceptCommandPeer()

ZygoteConnection
readArgumentList()

Zygote.java
forkAndSpecialize

--- child process ---

'WrapperInit.execApplication' or 'execStandalone'
# executing execv -> "/system/bin/app_process /system/bin --application --nice-name=xxx com.android.internal.os.WrapperInit <pipe fd> <targetSdkVersion> <shell args>
<shell args> : Args from Process.zygoteSendArgsAndGetResult <= which comes from startProcessLocked ("android.app.ActivityThread" ...)
# executing execv -> "/system/bin/dalvikvm -classpath '...' ...

--- execv ---

AppRuntime : AndroidRuntime -> "com.android.internal.os.RuntimeInit"

AndroidRuntime.start()
: startVm()
...
# starts Java startClass or starts "main" method.

RuntimeInit.main()

AndroidRuntime.cpp:: com_android_internal_os_RuntimeInit_nativeFinishInit()

app_main::AppRuntime.onStarted

AndroidRuntime.callMain()

*** <class>.main() *** => WrapperInit.main (see app_process argument above....).

RuntimeInit
wrapperInit()
applicationInit()
invokeStaticMain()

static ActivityThread.main : <= See WrapperInit.execApplication.

ActivityThread
attach()
ActivityManager.attachApplication() ==> ActivityManagerService.attachApplication()
# enter message loop!

ActivityManagerService
attachApplication()
attachApplicationLocked()
:--- attaching applications...

ActivityStackSupervisor.attachApplication()

ActivityStackSupervisor
realStartActivityLocked()

ActivityThread.scheduleLaunchActivity()
-> queue message : ActivityThread.LAUNCH_ACTIVITY

'Domain > Android' 카테고리의 다른 글

Multiple ABI and 64bit on Lolipop  (2) 2014.11.08
[Android] WindowManager & Display Area  (0) 2014.08.08
Create and use java library from packages.  (0) 2014.02.03
[NDK] 불편한 진실... 00  (0) 2013.11.25
Android resource compiler의 한계...  (0) 2013.11.09

조직 변경의 2가지 방법 - 역할 변경 vs. 사람 변경 - 에 대한 고찰.


먼저, "역할의 단위와 조직의 단위가 정확히 일치할 때 효율은 극대화 된다는 것"을 가정할 필요가 있다 - 이후 논의는 이 가정에 기반한다.

(물론, 필자가 이에 대한 논문을 찾아 본적도 없고, 연구결과가 있는지도 모르겠으나, 직관적으로 대부분의 사람들에 의해 그렇게 받아들여지고 있는 것 같다.)

또한, 큰 조직의 변경에 대해서 논할 만큼 나 자신이 경험이 많거나 연구를 많이 한 것이 아니라서 일단 고려하지 않고, 이 글에서는 소규모 조직 - 인원 50명 이하 정도가 4-5 개의 하위 조직으로 다시 나누어진 형태 - 의 변경에 대해서만 이야기 하고자 한다.


조직의 형태는 항상, 다양한 방식으로 변경된다.

많은 경우, 10명 이내/내외 의 인원이 가장 작은 단위의 조직이 되고, 이런 조직들이 다시 적게는 4-5개 많게는 10여개가 모여 다시 차 상위 조직이 되는 구조가 반복적으로 사용되어 하나의 큰 조직이 구성된다.

(옛날, 군대 조직 역시, 10부장, 100부장 등 10 단위로 조직이 구성되는 형태를 종종 볼 수 있다.)

그리고, 이런 소조직 별로 독립적인 역할이 부여되고, 이런 조직들의 유기적인 결합에 의해 다시 차상위 조직의 역할이 결정되는 구조를 가진다.

그런데, 업무와 역할이 항상 고정적일 수는 없고 변화가 생기게 마련이다.

어떤 역할은 그 규모가 확대되기도 하고, 또 어떤 역할은 그 규모가 축소되거나 혹은 아예 사라지기도 한다.

그리고, 이런 일들이 벌어질 때마다 소위 말하는 '조직변경'이라는 것이 생긴다.


'조직변경'의 생기게 되는 이유는 결과적으로 말하면, 역할의 단위와 조직의 단위가 잘 맞지 않기 때문이다.

역할은 내/외부 환경에 의해 계속 해서 바뀌게 되는데, 이때, 요구되는 역할의 단위가 현재 조직의 단위와 맞지 않다면, 조직의 효율을 높이기 위해, 기존 조직을 역할의 단위에 맞게 수정할 필요가 있다.

이때, "어떤 식으로 기존 조직을 변경할 것인가?"라는 문제가 생기게 되는데, 필자는 크게 두 가지 방법이 있다고 생각한다.

첫째, 역할의 변경.

둘째, 사람의 변경.


용어의 모호성을 없애기 위해 '조직 변경'을 '조직역할 변경'과 '조직원 변경'으로 구분하여 사용한다.

'조직역할 변경'이란, '조직'이 수행하는 역할이 변경되는 것을 뜻한다.

'조직원 변경'이란, '조직'의 사람(구성원)이 바뀌는 것을 의미한다.


[역할의 변경]

조직원을 기준으로 역할을 조정하는 방법이다.

즉, 조직 구성원의 변경을 최소화하고, 역할을 조정한다.


[사람의 변경]

역할을 기준으로, 조직원을 변경하는 방법이다.

위의 '역할의 변경'과 반대되는 개념이다.


일반적으로 '사람의 변경'이 '역할의 변경'보다 더 자주 사용되는 경향이 있는데, 내 생각에는, '사람의 변경'에 필요한 비용이 '역할의 변경'에 필요한 비용보다 예측하기 쉽고, 대부분의 경우 비용이 더 적게 들어가기 때문인 것 같다.


예를 들어, 아래의 상황을 가정해 보자.

* A팀(7명)과, B팀(10명) 이 존재한다.

* A팀은 kernel관련 업무를, B팀은 App.관련 업무를 맡고 있다.

* Kernel 업무는 많이 늘어나고 있어서 10명 정도의 인원이 필요한 반면, App. 업무는 줄어들고 있어서 7명 정도면 가능한 상황이다.


이와 같은 경우, 각각 아래와 같이 적용된다.

* 사람의 변경 : B팀의 인원 3명을 A팀으로 옮긴다.

* 역할의 변경 : B팀이 Kernel업무를 담당하고, A팀이 App. 업무를 담당하도록 한다.


어느쪽이 더 효율적이라고 보이는가?

아마, 대부분의 사람들이 아무런 의심이 없이 '사람의 변경'쪽을 택할 것이고, 나 역시, 높은 확률로 '올바른 선택'이 될 것이라고 생각한다.

그럼, 정말 '항상' 이와 같은 선택이 올바른 선택일까?

난, 여기에 의심을 품어 본다.


그렇다면, '왜' 대부분 '사람의 변경'쪽을 택하게 된 것일까?

아마도 아래와 같은 생각 때문이였을 것이다.


'사람의 변경'의 경우, 3명만, 새로운 업무에 적응하면 된다. 반면, '역할의 변경'의 경우 10 + 7 = 17 명 모두가 새로운 업무에 적응해야하고, 새로운 기술을 습득해야 한다.


상당히 합리적이로 옳은 판단의 근거라고 생각한다.

그렇지만, 정말로, 판단의 근거가 위의 내용밖에 없을까?

3명이 빠져 나감으로 인해서 발행하는 B팀의 사기 저하는? B팀의 인원이 A팀으로 옮길 시, 기존 팀원간의 불화에 따른 조직 문제는?

즉, 사람의 문제는 위의 판단에서 완벽하게 배제되어 있다.

다시 한번 말하지만, 위의 '예'의 경우는 '사람의 변경'이 높은 확률로 옳은 선택이라고 나 역시 생각한다.

그렇지만, '항상'은 아니라고 말하고 싶다.

'사람의 문제'에 따른 비용으로, 특수한 경우는 '역할의 변경'이 오히려 더 나은 경우도 분명히 존재할 것이다.

다만, 앞서 언급한 '사람의 문제'에 따른 비용이 예측되지 않기 때문에 사용되지 않을 뿐이라고 생각한다.


이런 종류의 연구를 좀더 조사해 볼 필요가 있어 보인다...


[ 내 개인적인 생각임을 미리 밝힌다. ]


HLD이든 LLD(Low Level Design)이든 결국 block diagram의 형태 (Component 단위든, Class단위의 UML이든...)일 수 밖에 없고, 얼마나 작은 단위의 block까지 명세하느냐에 따라 구분되어 질 것이다.

그럼 이런 block diagram에서 가장 중요한 요소는 무엇인가?

결국 'Design'이라는 측면에서 본다면, 각 block의 input과 ouput을 정의하는 것이다.

그리고, input/output(IO)를 정의한다는 것은, "이 모듈의 역할을 명확히 정의"하는 것과 같다.

더 나아가, block diagram이라는 것 역시 결국,이런 block간 IO의 연결일 뿐이다.


IO를 명확히 하는 것이 왜 SW design에 중요한가?

SW design은 결국 block 간 IO 연결의 형태로 표현되게 된다. 예를 들면,

"A block은 B block의 output을 input으로 받아서 C block과 D block에서 사용할 값들을 만들어 낸다"

같은 형태가 될 것이다.

이때, 내부 구현의 문제는 해당 block하나에만 영향을 끼친다. 즉 A block의 내부 구현이 문제라고 한다면, A block 만 열심히 다시 구현/설계하면 되는 것이다.

그런데, A block의 IO로 정의된 내용이 틀리다면, 예를 들면, "이 input으로는 기대하는 output을 만드는 것이 불가능 혹은 현실적이지 않다."하다면?

그렇다면, A와 연결된 이후 모든 block의 설계다 전부 연쇄적으로 변경되어야 한다.

즉, 소위 "설계 변경"이 일어날 수 밖에 없게 된다.


일반적으로, 내부 구현 문제는 논란의 여지도 많고, 개선의 여지도 많지만, "A라는 input으로, B라는 output을 만들어 낼 수 있느냐?"의 문제는 내부 구현에 대한 고민과 관계없이 판단 가능한 경우가 많다. 혹은 '불가능'하다는 판단은 못하더라도, '가능하다'라는 판단은 명확히 할 수 있다.

따라서, HLD는 이런 block의 IO정의에 대한 잘못된 판단 요소를 조기에 찾아서, 나중에 설계전체에 영향을 주게되는 일을 미리 방지하는게 목적이 된다.


비단 HLD뿐만 아니라, LLD 및 기타 모든 SW 설계역시 마찬가지일 것이다.

얼마나 큰 단위의 block으로 IO를 설계하느냐의 문제이지 block의 IO를 명확히 하는 것이 무엇보다 중요하다.


그럼, 여기서 좋은 설계자는 어떤 차이를 만들어 내는가?

좋은 설계자는, block간 R&R을 명확히 하고, block의 내부 구현에 대한 어느 정도 '감' 을 가지고 있어서, performance및 resource 사용을 최소화 할 수 있는 형태로 block을 나눌 수 있다.

특히, 여기서, '내부 구현에 대한 감'역시 상당히 중요한데, 때로는 이론적으로 상당히 뛰어난 설계를 했음에도 불구하도, 특정 block이 설계에 정의된 IO를 만족하기 위해 너무 많은 비용 - performance, memory 등 - 이 들어가는 경우가 발생할 수도 있다. 불가능 하다는 말이 아니다, 단지 현실적이지 않다는 뜻이다.

이럴 경우, HLD에서는 IO가 타당하다고 Review되었겠지만, 실제 구현단계에서 설계변경이 필요해지는 경우가 발생하게 되는 경우이다.

이때, 경험이 많은 설계자라면, HLD시점에서, 이런 구현단계의 구체적인 문제를 알지는 못하겠지만, "이런 식의 IO로 module을 설계하면, 뭔가 문제가 발생할 여지가 많아 보이는데... 찝찝하다..."라는 소위 '감'을 가질 수도 있고, 이 '감'에 따라서, 이런 '문제 발생 가능성이 높은 설계'를 피할 수 있을 것이다.



project전체에 대한 개괄적인 schedule이 "주 단위 (혹은 2 주 단위), 아주 큰 project라면 한달 단위"로 나올 수 있다면 HLD가 끝났다고 볼 수 있을 것이다. "schedule이 나왔다."는 말은, "어떤 식으로 개발 할 것이다." 라는 것이 정했졌다는 말이고, 그 말은, "전체적인 Design(큰 그림)이 그려졌다."라는 뜻이기 때문이다.

뭐, 이런 식의 기준이 틀렸을 수도 있지만... 일단 난 이 기준으로 판단하고자 한다.




LLDR은 아마도, 2~3일 단위 정도로 schedule이 나왔을 때 끝났다고 이야기 할 수 있지 않을까?


물론, 이 모든 것은 project 의 규모에 따라, flexible할 것이다....

system에 library를 embedding하고, 그것을 apk가 사용하고자 할때... (여러 apk에서 공유가능.)


Sample lib permission - com.my.lib.xml

<permissions>
    <library
        name="com.my.lib"
        file="/system/framework/com.my.lib.jar" />
</permissions>

library definition을 정의한, 위의 xml을 이용하면면, 해당 library를 system에 install하고, uses-library tag - AndroidManifest.xml - 를 이용해서 loading할 수 있게 된다.

Example uses-library

<uses-library
    android:name="com.my.lib"
    android:required="true" />

Sample Android.mk

LOCAL_PATH:= $(call my-dir)

# lib def
# -------
include $(CLEAR_VARS)
LOCAL_MODULE := com.my.lib.xml
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions
LOCAL_SRC_FILES := $(LOCAL_MODULE)
include $(BUILD_PREBUILT)

# the library
# -----------
include $(CLEAR_VARS)
LOCAL_MODULE := com.my.lib
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_SRC_FILES := $(call all-subdir-java-files)
include $(BUILD_JAVA_LIBRARY)

# the package
# -----------
include $(CLEAR_VARS)
LOCAL_MODULE := myApk
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := platform
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_REQUIRED_MODULES := com.my.lib.xml com.my.lib
include $(BUILD_PREBUILT)

Explanation

* Product가 myApk 를 설치한다면, 위의 두 module 역시 설치하도록 하기 위해서 LOCAL_REQUIRED_MODULES 를 사용한다.

* Android framework은 library, feature 등을 등록하기위해서, $(TARGET_OUT_ETC)/permissions/*.xml 을 scan한다.


이렇게 하면, myApk가 load될때, uses-library 에 기록된, com.my.lib 이 같이 load된다.
단, system class loader는 여전히 BOOTCLASSPATH를 기준으로 하고 있고, Application context가 제공하는 class loader를 사용해야 위의 library가 load된 class loader를 사용할 수 있다.

다시 말하면, library에 정의된 class를 loading하기 위한 방법/예 는 아래와 같다.(class loader를 이용한 것은 example을 보여주기 위한 것 뿐, 그냥 원하는 class를 import해서 사용해도 된다. - 이미 load된 class 이므로...)

ClassLoader cloader; // cloader = new PathClassLoader("/system/framework/com.my.lib.jar", ClassLoader.getSystemClassLoader()); => (*A) // cloader = ClassLoader.getSystemClassLoader(); => (*B) // cloader = getApplicationContext().getClassLoader(); => (*C) cloader.loadClass("com.my.lib.SampleClass");


*A, *C : 정상적으로 SampleClass가 loading 됨.

*B : SampleClass를 load할 수 없음.

Done.



* First of all, check your kernel configuration.

- CONFIG_MODULES=y
- CONFIG_MODULE_UNLOAD=y    <- Optional

* Create your Kernel module code and Makefile


If you have luck, everything should be OK. But...


CASE: error at insmod?

insmod: init_module 'hello.ko' failed (Exec format error) in android
kernel message : unknown relocation: 27
Hmm, then you should check kernel functions you are using in the module (ex. printk)
You can check relocation type with following command (ex. printk)

readelf -r hello.ko | grep prink

Then, result may like below

xxxxxxxx  xxxxxxxx R_ARM_PLT32       00000000   printk

For your information : number 27 indicates R_ARM_PLT32.

Then, what's the problem?
ELF for the ARM Architecture(IHI0044E_aaelf.pdf) says that R_ARM_PLT32 is deprecated.
And, Linux kernel doesn't handle R_ARM_PLT32 at module.c : apply_relocation() function
We have two workaround for this issue.

* Update toolchains.
In my case, I faced this issue when I used gcc-4.4.3. And, gcc-4.7 resolved this issue.
(You can see, relocation type is changed into R_ARM_CALL instead of R_ARM_PLT32.)

* fpic / fno-pic HACK.
If you are in trouble with upgrading your toolchains, here is workaround (tested on gcc-4.4.3).
Add, -fpic or -fno-pic at CFLAGS_MODULE in your module Makefile like below.

make CFLAGS_MODULE=-fpic xxxx modules

Then, like above - updating toolchains -, relocation type is changed :-)



Java에서는 selective compile이 되지 않는다.

대신, compile time에 결정되는 것들을 이용해서 selective compile과 비슷한 효과를  만들어 낼 수 있다.

예를 들면,


Config.jar
----------
class Config {
...
    public static final boolean DEBUG = false;
...
}


Main.java
---------
...
if (Config.DEBUG) System.out.println("debug line...");
...

자, 그러면 어디서 어디까지 Compile time 에 결정되는가?

openJDK 1.6에서 test를 해 보면, 아래의 것들에 대해서는 compile time에 결정되는다는 것을 확인했다.


primitive types (int, boolean, long...) + String.


여기서 의외의 부분이 'String'인데...

실제, 아래와 같이 static final String 변수로 test해 보면, 이것을 확인할 수 있다.

Config.jar
----------
class Config {
    ...
    public static final String MODE = "debug";
    ...
}


Main.java
---------
...
if (Config.MODE.equals("debug")) System.out.println("debug mode...");
...

생각해보면, String class자체가 constant class이므로 - 한번 생성되면, 내용이 바뀌지 않는 class - 위와 같은 compile time check가 가능하지 않을까... 생각이 든다.


아래는 위의 내용에 대한 상세 test이다.

test/lib/Config.java -------------------- package test.lib; public class Config { public static final int TEST_INT_0 = 1; public static final boolean TEST_BOOL_0 = false; public static final String TEST_STR_0 = "test-str-0"; public static int TESTV_INT_0 = 1; public static boolean TESTV_BOOL_0 = false; public static String TESTV_STR_0 = "test-str-0"; } ===> export to 'test0.jar' package test.lib; public class Config { public static final int TEST_INT_0 = 10; public static final boolean TEST_BOOL_0 = true; public static final String TEST_STR_0 = "test-str-1"; public static int TESTV_INT_0 = 10; public static boolean TESTV_BOOL_0 = true; public static String TESTV_STR_0 = "test-str-1"; } ===> export to 'test1'jar Main.java ---------- import test.lib.Config; public class Main { public static void main(String[] args) { if (Config.TEST_STR_0.equals("test-str-0")) System.out.println("Config:TEST_STR_0 : IF path - test-str-0"); else System.out.println("Config:TEST_STR_0 : ELSE path - test-str-0"); if (Config.TEST_INT_0 > 5) System.out.println("Config:TEST_INT_0 : IF path - > 5"); else System.out.println("Config:TEST_INT_0 : ELSE path - > 5"); if (Config.TEST_BOOL_0) System.out.println("Config:TEST_BOOL_0 : IF path - true"); else System.out.println("Config:TEST_BOOL_0 : ELSE path - "); if (Config.TESTV_STR_0.equals("test-str-0")) System.out.println("Config:TESTV_STR_0 : IF path - test-str-0"); else System.out.println("Config:TESTV_STR_0 : ELSE path - test-str-0"); if (Config.TESTV_INT_0 > 5) System.out.println("Config:TESTV_INT_0 : IF path - > 5"); else System.out.println("Config:TESTV_INT_0 : ELSE path - > 5"); if (Config.TESTV_BOOL_0) System.out.println("Config:TESTV_BOOL_0 : IF path - true"); else System.out.println("Config:TESTV_BOOL_0 : ELSE path - "); } } ========================================== $ javac -classpath test0.jar Main.java $ java -classpath .:test0.jar Main Config:TEST_STR_0 : IF path - test-str-0 Config:TEST_INT_0 : ELSE path - > 5 Config:TEST_BOOL_0 : ELSE path - true Config:TESTV_STR_0 : IF path - test-str-0 Config:TESTV_INT_0 : ELSE path - > 5 Config:TESTV_BOOL_0 : ELSE path - true $ $ java -classpath .:test1.jar Main Config:TEST_STR_0 : IF path - test-str-0 <--- 바뀌지 않음. (변수 값이 compile time에 이미 binding되어 있음.) Config:TEST_INT_0 : ELSE path - > 5 <--- 상동 Config:TEST_BOOL_0 : ELSE path - true <--- 상동 Config:TESTV_STR_0 : ELSE path - test-str-0 Config:TESTV_INT_0 : IF path - > 5 Config:TESTV_BOOL_0 : IF path - 참고 : TESTV_XXX_0를 없애면, test0.jar 혹은 test1. jar를 'java'의 'classpath' 에 명시해 주지 않더라도 정상수행 된다. ex. $ java Main 왜냐하면, 이미 모든 code가 compile time에 binding어 있는 상태으므로, Runtime에는 더 이상 'test.lib.Config' 를 참조하지 않기 때문이다.

위와 같은 static final 변수를 runtime binding하기 위한 방법의 한 예로 reflection을 들 수 있다.

reflection을 이용해서 해당 변수 field를 접근하면, static final로 선언된 변수이지만, runtime binding이 가능하다.

일단 man page를 유심히 살펴보자.

FIFO(7)                             Linux Programmer's Manual                            FIFO(7)

NAME
       fifo - first-in first-out special file, named pipe

DESCRIPTION
       A  FIFO  special  file (a named pipe) is similar to a pipe, except that it is accessed as
       part of the file system.  It can be opened by multiple processes for reading or  writing.
       When  processes  are  exchanging data via the FIFO, the kernel passes all data internally
       without writing it to the file system.  Thus, the FIFO special file has  no  contents  on
       the  file  system;  the file system entry merely serves as a reference point so that pro‐
       cesses can access the pipe using a name in the file system.

       The kernel maintains exactly one pipe object for each FIFO special file that is opened by
       at  least one process.  The FIFO must be opened on both ends (reading and writing) before
       data can be passed.  Normally, opening the FIFO blocks until  the  other  end  is  opened
       also.

       A  process can open a FIFO in nonblocking mode.  In this case, opening for read only will
       succeed even if no-one has opened on the write side yet, opening for write only will fail
       with ENXIO (no such device or address) unless the other end has already been opened.

       Under  Linux,  opening  a  FIFO for read and write will succeed both in blocking and non‐
       blocking mode.  POSIX leaves this behavior undefined.  This can be used to  open  a  FIFO
       for  writing  while there are no readers available.  A process that uses both ends of the
       connection in order to communicate with itself should be very careful to avoid deadlocks.

NOTES
       When a process tries to write to a FIFO that is not opened for read on  the  other  side,
       the process is sent a SIGPIPE signal.

       FIFO  special files can be created by mkfifo(3), and are indicated by ls -l with the file
       type 'p'.

SEE ALSO
       mkfifo(1), open(2), pipe(2), sigaction(2), signal(2), socketpair(2), mkfifo(3), pipe(7)

COLOPHON
       This page is part of release 3.35 of the Linux man-pages project.  A description  of  the
       project, and information about reporting bugs, can be found at http://man7.org/linux/man-
       pages/.

Linux                                      2008-12-03                                    FIFO(7)

주의할 사항은 위에서, Bold로 표시해 두었다.


먼저 "read/write 양쪽이 열리기 전까지는 open이 block된다."는 말이 무슨 말일까?

아래 코드를 살펴보자.

int main()
{
	char msg[64];
	int fd;
	int rd;

	if (-1 == mkfifo("./fifo",0666))
		return 1;

	if (-1 == (fd = open("./fifo", O_RDWR))) // <--- (*A)
		return -1;
	printf("Before loop\n"); // <--- (*a)
	for (;;) {
		if (-1 == (rd = read(fd, msg, sizeof(msg))))
			return 1;
	}
	unlink("./fifo");
	return 0;
}

위의 코드는 실행시키면 바로 , (*a)가 수행된다.

그렇지만, (*A)의 open mode를 O_WRONLY 나 O_RDONLY 로 하면, pipe의 다른 한쪽 (read 혹은 write)가 열리기 전까지는 (*a)가 수행되지 않는다 (open에서 block된 상태) 는 말이다.



그리고, "read only의 경우 nonblocking mode로 open이 가능하다고 wirte는 안된다." 는 어떤식 문제를 가져 오는가?

앞서 이야기한 것처럼, 위의 코드에서, (*A)를 O_RDONLY로 하면, write pipe가 열리기 전까지는 (*a)가 수행되지 않는다.(block된 상태).

그렇지만, "$ echo hello > ./fifo" 같은 명령을 통해 write side를 일단 열개 되면, loop로 들어가게 되는데,

문제는, read가 NONBLOCKING으로 동작한다는 것이다.

즉, 더 이상 읽을 것이 없음에도 불구하고, block이 되지 않고 busy loop를 돌게 된다.
하지만, 위의 예시처럼, O_RDWR로 열게 되면, 더 이상 읽을게 없을 경우, read 에서 block 되어, 일반적으로 기대하는 방식으로 동작하게 된다.


추가적으로, File이란, System-wide한 resource이다.

따라서, 두 개 이상의 Process가 하나의 Pipe에 대해 동시에 read를 하고 있다면, 먼저 읽는 Process가 Pipe의 내용을 읽어가게 되고, 다른  Process들은 내용을 읽지 못한다.


'Language > C&C++' 카테고리의 다른 글

'malloc' and 'Segmentation fault'  (0) 2014.07.25
[GCC] Initialize array...  (0) 2014.07.25
[c/c++] file copy using 'sendfile' on linux.  (0) 2013.12.17
일반 file에 대한 select/poll...  (0) 2013.11.14
Using allocated memory like 2-D array.  (0) 2013.07.10

+ Recent posts