In case that sqlite database is quite big, preparation for using cursor requires lots of time-consuming-operation.

So, developer may considering about asynchronous way - showing progress while preparing cursor in background.

Then, what is 'Really' time-consuming-operation in preparing cursor?

As my understanding, in Android sqlite cursor, the answer is first 'fillWindow' operation of SQLiteCursor.


Notable thing is, at sqlite cursor, creating cursor doesn't do any real (or practical) thing.

So, creating cursor is done very quickly.

By the way, Sqlite cursor do 'fillWindow' operation at the moment of first 'getCount()' call - See SQLiteCursor.java.

Therefore, to prepare cursor in background, not only creating cursor but also calling first 'getCount()' function are required.


Here is sample function for reference.

    public void
    reloadCursorAsync() {
        DiagAsyncTask.Worker worker = new DiagAsyncTask.Worker() {
            private Cursor newCursor;
            @Override
            public void onPostExecute(DiagAsyncTask task, Err result) {
                changeCursor(newCursor);
            }
            @Override
            public Err doBackgroundWork(DiagAsyncTask task, Object... objs) {
                newCursor = createCursor();
                newCursor.getCount();
                return Err.NO_ERR;
            }
        };
        new DiagAsyncTask(mContext, worker,
                          DiagAsyncTask.Style.SPIN,
                          R.string.loading, false)
        .execute();
    }



Android document says several advantages of android:targetSdkVersion. But, I faced following issue.


Here is my case.


android:minSdkVersion="10"

android:targetSdkVersion="15"

...

<activity ...

android:configChanges="orientation|keyboardHidden"

...


Since v3.2 ScreenSize is additionally required at configChanges to handle orientation change.

But, in this case, minSdkVersion is "10" so, ScreenSize cannot be used.

The problem is that targetSdkVersion is "15".

So, framework doesn't call onConfigurationChanged() callback when orientation is changed.

This is unavoidable and unexpected.


Eventually, I gave up using 'targetSdkVersion'.


I'm not sure there are any other workarounds for these cases, but after experiencing above case, I am trying to Not-To-Use targetSdkVersion option...

Usually, parent thread who creates child thread, wants to get execution result (return value) of child thread.

For this, pthread_join() is used.

In most cases, pthread_create() / pthread_join() are used like a kind of pair.

So, habitually, developer tends to ignoring about detaching child thread.


But, in some cases, parent thread doesn't care about the result of child thread, and pthread_join() is not used.

In this case, child thread should be detached from parent thread to say that "parent thread doesn't care about the result".

Why this is required?

To pass return value of child thread, some  memory should be used to store result until it is consumed.

So, detaching means avoiding keep that memory.


In POSIX thread, there are two ways for detaching thread.

one is using pthread_detach() function, and the other is using PTHREAD_CREATE_DETACHED attribute at pthread_create().


Always keep in mind that child thread keep some amount of memory to pass it's return value to parent.


Process version of this case is "Zombie Process". See this post for details.

This post is history of registering Android App to Google Play.

I hope that this post is useful to developers who has a plan to register App to Google Play.



I registered one free App. to Google Play and after some time - about a-week-after? - I received below E-mail from Google.




This is a notification that your application, Youtube Music Player, with package ID free.yhc.youtube.musicplayer, has been removed from the Google Play Store.


REASON FOR REMOVAL: Violation of the intellectual property and impersonation or deceptive behavior provisions of the Content Policy.

All violations are tracked. Serious or repeated violations of any nature will result in the termination of your developer account, and investigation and possible termination of related Google accounts.

If your developer account is still in good standing, you may revise and upload a policy compliant version of your application as a new package name. Before uploading any new applications, please review the Developer Distribution Agreement and Content Policy.

If you feel we have made this determination in error, you can visit the Google Play Help Center article for additional information regarding this removal.

The Google Play Team




Issued application is the one below


YoutubeMusicPlayer-v1.3.0-2.apk


Main feature of this application is "making playlist of youtube videos and play those continuously without video (only audio) in background.


Actually at that moment, I didn't have any idea regarding what are problems.

So, some E-mails are additionally sent/received

Here is E-mail thread between Google and me.




Hi Google

I received below mail.

Could you describe details about the violations of application free.yhc.youtube.musicplayer.

Actually, I do not know what are issued points.

My developer account is yhcting77@gmail.com.

Thank you in advance and sorry for inconvenience.

YH Cho.




Hi Ting,

Thank you for your note.

We appreciate the opportunity to review your appeal regarding the removal
of your app from Google Play. After an investigation, we affirm our
initial decision and will not be reinstating your application at this
time.

If your developer account is still in good standing, and the nature of
your application allows for you to upload a new, compliant version of this
application to Google Play; please apply the following Content Policy
guidelines to future releases:

Google Play Android Developer Program Policy
(see "Impersonation or Deceptive Behavior"):
http://play.google.com/about/developer-content-policy.html

Regards,
The Google Play Team




Hi Google.


I'm sorry for continuously bothering you.
I reviewed "Google Play Android Developer Program Policy".
In my opinion at this moment, my violation is - concretely -  "Youtube-TM-like-application-icon and App name are used, and this may lead users to misunderstand this App. has some relation with Google.".
Am I right?
As my previous e-mail, please let me know What are issued points and problems concretely and that will be very helpful for me to update application to obey Google's policy.
I really would like to go with Google's Policy but to do it, at first I should know "What is real problem".

Thank you in advance and I'm sorry again for bothering you.
Regards.
YH Cho.




Hi Ting,

Thank you for your note. Your application's icon and title could be
misleading to some users and lead them to think that your app is
authorized by another organization. Please be aware of the "Impersonation
or Deceptive Behavior" section of the Content Policy as you publish your
applications:
http://www.android.com/us/developer-content-policy.html#impersonation



Regards,
The Google Play Team



As described as above E-mail thread, at this moment, I think app name and app icon is mainly issued points of policy violation.

So, I will register App with fix of known issued point - app icon and name.

But, I am not sure that icon and name are really all issued cases.

If there are any other issues I will update those here to leave history and data to help understanding regarding Google Play Policy.

Now, app is re-registered to Google Play. Here



Developer cannot guess when Java GC is run. So, in Android, Bitmap class has special interface 'recycle' to reuse memory as soon as possible when Bitmap is not used anymore.

Based on similar reason, we need to recycle ImageView.


But, here are some points to consider when recycling memory of ImageView.

* ImageView has drawable - most case it is instance of BitmapDrawable - regardless of it's input-resource-type - ex setImageResource(),  setImageURI() or setImageBitmap(). And, ImageView has BitmapDrawable not only the case of setImageBitmap but also some other cases - ex setImageResource.



* Sample code to recycle Bitmap of ImageView is something like below


        // true == (v instanceof ImageView)

        Drawable d = v.getDrawable();

        if (d instanceof BitmapDrawable) { // to make sure.

            BitmapDrawable bmd = (BitmapDrawable)drawable;

            Bitmap bitmap = bmd.getBitmap();

            bitmap.recycle();

        }


* setImageDrawable(), setImageResource() and setImageURI() compares new-input and current used one.

So, if new one is same with current one, ImageView doesn't do anything. (See ImageView.java for details.)


* ImageView doesn't provide any interface to compare that given one is same with current one or not.


Because of above 4 reasons, recycling memory of ImageView is not straight-forward.

Simplest way, in my opinion, is feed newly-created-Bitmap-instance to ImageView whenever changing drawable, and recycle old one.

Because drawable is newly-created-instance, we don't need to compare new one with old one.

So, blind recycling old one, is ok in this case.


Here is sample function.


    public static void

    setThumbnailImageView(ImageView v, byte[] imgdata) {

        Bitmap thumbnailBm;

        if (null != imgdata && imgdata.length > 0)

            thumbnailBm = BitmapFactory.decodeByteArray(imgdata, 0, imgdata.length);

        else

            thumbnailBm = BitmapFactory.decodeResource(Utils.getAppContext().getResources(),

                                                       R.drawable.ic_unknown_image);


        // Recycle old Bitmap

        Drawable drawable = v.getDrawable();

        if (drawable instanceof BitmapDrawable) { // to make sure.

            BitmapDrawable bmd = (BitmapDrawable)drawable;

            Bitmap bitmap = bmd.getBitmap();

            bitmap.recycle();

        }


        // change to new one.

        v.setImageBitmap(thumbnailBm);

    }

‎"유투브에서 동영상을 다운로드하는 앱들이 인터넷을 찾아보면 널렸는데 왜 마켓에는 없을까..."라는 의문에서... "구글이 자기들 이익을 위해서 그렇게 했다..." 라고 생각했었는데... 오늘 다시 한 번 곱씹어 보니 다른 결론을 내렸다.

유투브의 동영상의 소유권은 구글에 있지 않고, 업로더에 있다고 알고 있다.
그리고 유투브는 소유권자의 동의를 얻어 그 동영상을 스트리밍 서비스 하는 것이고...

그런데, 원칙적으로 "소유권이 없는 컨텐츠를 다운로드해서 보관하고 있는 것 자체는 불법이다."라는 관점에서 보면... 유투브는 업로더에서 스트리밍 서비스에 관한 동의만을 얻은 것이기 때문에, 구글이 다운로드하는 수단을 어떠한 방법으로든 제공한다면, 업로도(소유권자)와의 계약(? - 이걸 계약이라고 말한다면...)관계에서 문제의 소지가 있을 것 같다.


따라서, 마켓에서 유투브 동영상을 다운로드 가능하게 하는 앱을 서비스 한다면, 그 자체가 소위 "수단"을 제공하는 것이라고 해석될 수도 있지 않을까? 이런 문제 때문에 구글이 마켓에서 유투브 다운로드 프로그램을 없애 버리는게 아닐까?

구글이 자신들의 이익때문에 그러는게 아니라, 컨텐츠 소유권자의 권익 보호를 위해서 그러는거다... 라는 긍정적인 방향에서도 볼 수 있겠다....

한편, T-store에 Tubemate(유투브 다운로드 앱)가 버젓이 올라와 있는 건 SKT는 유투브와 전혀 관계없기 때문에 '수단'을 제공할 때 생길 수 있는 어떠한 제약도 없기 때문이 아닐까?


뭐 나름 주저리 주저리 이런 결론도 가능하다.. 라고 생각했다는...ㅋㅋ - 물론 아무런 근거도 없는 나 혼자만의 생각... ^^



뭐.. 여러가지 SW Branch 정책들이 있을텐데... (하나의 branch에 전부 때려 잡아 넣고 개발하는 회사라면... 쩝 뭐 특별히 할 말 없다)

보통의 경우 SW branch는 아래와 같은 형태를 가진다.


+------------------------------ sub branch

/ ---------+-------------------+-------------------------------------- main branch

\

+------------------------------------ sub branch


기본적인 형태는 같지만, 정책적인 측면으로 보면 크게 두 가지로 분류할 수 있다.


1. 큰 main branch 정책

main branch가 기능 구현의 주요 작업 공간이 되는 방법.

main branch 가장 자주 update된다.

- feature이 대부분 main branch에서 개발되므로, 다른 외부 project branch의 개수가 적은 편이다.


2. 작은 main branch 정책

sub-branch가 기능 구현의 주요 작업 공간이 되는 방법.

- main branch가 가장 뜸하게 update되고 엄격하게 관리된다.

- feature들을 main branch와는 별도로, 서로 다른 project branch로 관리한다.


위의 두 가지 모두 기본적인 업무 방식은 같다.

1. 특정 시점에서 main branch에서 branch out

2. 필요한 새로운 feature들을 구현하거나, 다른 feature branch로 부터 가져오고, stability를 향상시킴

3. software release

4. 필요한 patch들을 main branch에 다시 merge함.


그렇지만, 위와 같이 서로 다른 방식의 정책을 적용하다보면, branch의 특성이 서로 달라진다.

1. 큰 main branch 정책

- main branch는 feature위주로 관리된다.

- main branch는 거의 모든 feature들을 가진다.(feature들의 super-set)

- main branch가 가장 많은 버그를 가진다.

- main branch의 코드 size가 sub branch에 비해 크다.

- 보통 branch out의 기준은 feature가 된다. 즉, 특정 feature가 available한 시점을 기준으로 main branch로 부터 branch out한다.

장점 : Integration에서 발생가능한 문제들이 main branch에서 충분히 다루어진 상태이므로, sub branch에서 이 문제에 대한 risk가 적다.

단점 : main branch의 stability관리가 어렵다. 그래서, sub branch에서 stability를 위한 비용이 크다.


2. 작은 main branch 정책

- main branch는 stability위주로 관리된다.

- main branch는 최소한의 feature만을 가진다.

- main branch의 stability가 가장 뛰어나다.

- branch out의 기준은 feature보다는 stability가 된다.

장점 : main branch의 stability가 충분히 좋으므로, sub branch에서 stability문제가 발생하더라도, main branch를 기준으로한 debugging이 가능하므로, 적은 비용으로 sub branch의 stability를 유지할 수 있다.

단점 : integration에 따른 문제들이 충분히 드러난 상황이 아니므로, sub branch에서 이것과 관련된 많은 문제들이 나올 수 있다.


branch 정책을 결정할 때는 위의 두 가지 요소를 충분히 고려할 필요가 있다.

stability에 대한 비용 vs. integration에 대한 비용

그리고 거기에 따라, branch 정책을 정할 필요가 있다.


개인적으로 봤을 때, 대부분의 경우, stability를 위한 비용이 integration비용에 비해 상당히 비싸다.

따라서, 작은 main branch정책이 더 좋은 것 같은데.... 


어지간한 규모가 있는 회사라면, 이유나 목적이야 어떻든 공식적으로는 직원들의 역량 강화라는 명목으로 교육을 실시하는 경우가 많다. 하루짜리 단일성 교육부터 8시간 4~5일짜리 장기교육까지... 가격도 어마어마하다.

그런데 그런 교육의 효과는 어떠한가? 그만한 값어치를 하는가? 교육을 받는 당사자의 생각도 중요하지만, 돈을 들여 교육을 실시한 회사의 입장은 어떤가? 직원들을 일주일간 업무에서 빼고, 비싼 강의료를 지불하면서까지 실시한 교육의 그만한 가치가 있다고 생각하는가?

대부분의 경우 만족스럽지 못할 것이다. 왜 그럴까?

아래는 전부 내 개인적인 생각으로 어떠한 근거도 뒷받침할 자료도 없다. -_-;


과거 학생 때를 돌이켜 보면, 공부에서 가장 중요한 것은 예습/복습이다. 회사에서 실시하는 교육도 그 연장선상에 있다고 본다.

보통 회사에서 실시하는 교육자체가 직무관련 교육인 경우가 많기때문에 어느정도 예습의 효과는 있다고 본다. - 물론, 교육의 커리큘럼을 따라서 제대로 예습한다면 더 좋겠지만...

그런데 문제는 '복습'이 전혀 이루어 지지 않는 다는 것이다.

1주일 교육이라면, 그 교육이 끝난 뒤에는 바로 업무에 복귀하게 되고, 일반적으로 회사에서 수행하는 없무는 교육받은 내용과 어느 정도 거리가 있기 마련이기 때문에 교육기간 동안 습득한 내용을 자기 것으로 만들 시간이 거의 없다.

복습의 효과에 대해서는 이미 수많은 검증된 연구결과들이 있다. 복습을 전혀 하지 않는 것과 어느 정도 복습을 하는 것은 교육의 효과와 더불어 내용을 기억하는 기간에 엄청난 차이를 가져온다.


그래서 난, 회사가 어차피 직원들을 교육시키는데 비용을 투자하기로 결정했다면, 약간의 비용 - 직원들이 업무 외의 일을 할 수 있는 추가적인 시간 - 을 더 투자해서 '강의'를 통한 교육에 이어서 교육의 내용을 소위 '복습'할 수 있는 공식적인 기간을 주는 것이 필요하다고 본다.

물론, 회사에서 하는 일이니 측정가능한 '성과'는 있어야 할 것이다.

그것이 세미나의 형태가 되었건 아니면, 잘 정리된 문서가 되었건, 아니면 교육의 내용을 기반으로한 간단한 소프트웨어 툴이 되었건, 자신이 습득한 내용을 정리하고 적용해 볼 수 있는 시간을 공식적으로 주고 또 장려할때, 교육의 성과가 극대화 될 것이라고 본다.


구체적으로 예를 들어보면, 하루 8시간 5일 교육을 가정하면, 이후 일주일 - 5 업무일 - 은 '복습'의 시간으로 할애해서, 피 교육생들이 교육시간에 배웠던 것들을 이것저것 실습해보고 적용해 보도록 하는 것이다...


쩝... 전부 내 개인적인 생각들이였다... 교육 들을때만 고개를 끄덕이고, 끝나고 일주일만 지나면, 뭘 들었는지조차 기억에서 희미해지는 그런 비효율적인 교육들을 보면서 답답한 마음에 끄적여 본다...

현재 영문 번역이 진행중입니다.... (English translation is under construction.)


Big Picture

-----------


     0 +-----------------------+      Block group details

| |     /

  1024 +-----------------------+    / +-------------------------------+

| super block |   / | Super block copy |

  2048 +-----------------------+  / +-------------------------------+

| | / |    Group Descriptor Table |

+-----------------------+/ +-------------------------------+

| Block group 0 | | Block Bitmap |

+-----------------------+ +-------------------------------+

| Block group 1 | | Inode Bitmap |

+-----------------------+ +-------------------------------+

| ... | | Inode Record |

+-----------------------+ +-------------------------------+

| Block group n | +---+--| |-----+

+-----------------------+ |   | | Inode Table |     |

|   | | |<-+  |

|   | +-------------------------------+  |  |

|   | | .... |  |  |

|   | +-------------------------------+  |  |

|   +->| File Content |  |  |

| +-------------------------------+  |  |

| | ... |  |  |

| +-------------------------------+  |  |

+----->| Directory entries |--+  |

+-------------------------------+     |

| ... |     |

+-------------------------------+     |

| File Content |<----+

+-------------------------------+



ext4 raw block에 대해서 분석해 놓은 자료를 찾기 힘든관계로, kernel source를 분석한 내용을 바탕으로 정리해 본다.

(특히 directory쪽에 집중했다.)

==> 개인적인 분석에 근거한 것이므로 정확도는 보장할 수 없다는... -_-;

In my case, it's very difficult to find documents regarding ext4 raw block. So, I described some parts of them based on Linux kernel source codes. (especially, focusing on directory part).
==> This is just personal analysis. So, I can't guarantee correctness.. :-)


File system의 기본 정보는 super block에 저장되어 있다. ext4 super block에 대한 detail은 Linux kernel에서 "struct ext4_super_block"을 참고하면되고 여기서는 몇몇개만 언급해 보면

s_log_block_size    : file system block size (단위 KB). 보통 4. 즉 4KB

s_blocks_per_group  : 하나의 block group을 이루는 block 개수

s_inodes_per_group  :

s_inode_size        : ext4의 경우 256

s_inodes_count      : total inode count

Super block contains basic information of file system. For ext4 super block, struct ext4_super_block of Linux kernel source, is good reference for details. Let me describe some of them.

s_log_block_size    : size of file system block size (KB). usually 4 - 4KB

s_blocks_per_group  : number of blocks in a 'block group'

s_inodes_per_group  :

s_inode_size        : 256 in case of ext4

s_inodes_count      : total inode count



ext4의 경우 기존의 indirect block의 개념 + extent라는 개념을 사용하고 있다.

ext4 uses existing indirect-block + extent.

(details for indirect block : http://computer-forensics.sans.org/blog/2008/12/24/understanding-indirect-blocks-in-unix-file-systems/)


먼저 http://computer-forensics.sans.org/blog/2010/12/20/digital-forensics-understanding-ext4-part-1-extents <= 이곳의 내용을 읽어보도록 하자.

Please read above link before moving forward.


아래는 ext4.h 에서 발췌한 내용이다.

Followings comes from ext4.h


... (생략 skip) ...

/*

 * Constants relative to the data blocks

 */

#define EXT4_NDIR_BLOCKS 12

#define EXT4_IND_BLOCK EXT4_NDIR_BLOCKS

#define EXT4_DIND_BLOCK (EXT4_IND_BLOCK + 1)

#define EXT4_TIND_BLOCK (EXT4_DIND_BLOCK + 1)

#define EXT4_N_BLOCKS (EXT4_TIND_BLOCK + 1)


...(생략 skip)...

struct ext4_inode {

... (생략 skip) ...

__le32 i_blocks_lo; /* Blocks count */

... (생략 skip) ...

__le32 i_block[EXT4_N_BLOCKS]; /* pointers to blocks */

... (생략 skip) ...

}


ext4의 inode에서 실제 block을 나타내기 위해서 60 bytes (sizeof i_block)를 사용하고 있는다.

여기서 EXT4_N_BLOCKS == 15 이므로 15 * 4 = 60 이 block을 가리키는 포인터로 사용된다.

전통적인 direct/indirect block을 위해서라면, 15 index를 사용할 수 있다.

60 bytes (sizeof i_block) is used to represent real block at ext4-inode.

EXT4_N_BLOCKS == 15. Therefore 15 * 4 = 60 is used as pointers indicating real block.

For direct/indirect block, 15 indexes are available.


i_blocks_lo 의 경우 i_blocks_high와 함께 이 inode에 할당된 block의 count를 가지고 있다.

여기서 주의할 점의 여기서 사용되는 block의 size는 일반적인 super block에 저장된 그 block size(보통 4KB)가 아니라, 512 bytes라는 점이다. ext4/inode.c를 보면, "<< 9" 혹은 ">> 9"등의 표현을 종종 볼 수 있는데, 이것이 바로 512 bytes 단위를 표현하기 위한 것이다. 추가적으로 아래를 참고하자.
inode_add_bytes() : fs/stat.c ==> 512라는 숫자가 직접적으로 등장하기도 한다.
reserve_backup_gdb() : ext4/resize.c ==> "inode->i_blocks += reserved_gdb * sb->s_blocksize >> 9;"

i_blocks_lo and i_blocks_high has count of blocks allocated for the inode.

One thing to note, is block size here is NOT 4KB - usually mentioned at super block - but 512 bytes.

Expressions like "<< 9" and ">> 9" are often shown at ext4/inode.c, and this represents 512 bytes block size.

For additional information, please see

inode_add_bytes() : fs/stat.c ==> number '512' is shown up explicitly.

reserve_backup_gdb() : ext4/resize.c ==> "inode->i_blocks += reserved_gdb * sb->s_blocksize >> 9;"


한가지 더 짚고 넘어갈 내용은, i_blocks_lo가 meta data를 위한 block까지 포함하느냐? 의 문제이다.

ext4_ext_swap_inode_data : ext4/migrate.c를 보면, 아래의 코드라인이 보인다.

One more thing to consider is whether i_blocks_lo includes block for meta data or not?
Following lines of codes are observed at 
ext4_ext_swap_inode_data : ext4/migrate.c

/*

 * Update i_blocks with the new blocks that got

 * allocated while adding extents for extent index

 * blocks.

 *

 * While converting to extents we need not

 * update the orignal inode i_blocks for extent blocks

 * via quota APIs. The quota update happened via tmp_inode already.

 */

spin_lock(&inode->i_lock);

inode->i_blocks += tmp_inode->i_blocks;

즉 meta data를 위한 block역시 i_blocks_lo에 count된다는 뜻이다.

주의 : 그렇지만, meta data는 file의 real data가 아니므로 logical block index번호를 가지지는 않는다.
That is, i_blocks_lo includes blocks for meta data.
Note : But, meta data is NOT real data of file. So, it doesn't have logical block index.

inode의 extent의 경우 60 bytes를 아래와 같은 format으로 사용한다.

Extent for inode uses 60 bytes as following format.


|<- 12 bytes ->|<- 12 bytes ->|<- 12 bytes ->|<- 12 bytes ->|<- 12 bytes ->|

+--------------+--------------+--------------+--------------+--------------+

|    header    |    extent0   |    extent1   |    extent2   |    extent3   |

+--------------+--------------+--------------+--------------+--------------+


관련 code는 아래와 같다 (from ext4_extents.h)

Here are related code (from ext4_extents.h)

/*

 * ext4_inode has i_block array (60 bytes total).

 * The first 12 bytes store ext4_extent_header;

 * the remainder stores an array of ext4_extent.

 */


/*

 * This is the extent on-disk structure.

 * It's used at the bottom of the tree.

 */

struct ext4_extent {

__le32 ee_block; /* first logical block extent covers */

__le16 ee_len; /* number of blocks covered by extent */

__le16 ee_start_hi; /* high 16 bits of physical block */

__le32 ee_start_lo; /* low 32 bits of physical block */

};


/*

 * This is index on-disk structure.

 * It's used at all the levels except the bottom.

 */

struct ext4_extent_idx {

__le32 ei_block; /* index covers logical blocks from 'block' */

__le32 ei_leaf_lo; /* pointer to the physical block of the next *

* level. leaf or next index could be there */

__le16 ei_leaf_hi; /* high 16 bits of physical block */

__u16 ei_unused;

};


/*

 * Each block (leaves and indexes), even inode-stored has header.

 */

struct ext4_extent_header {

__le16 eh_magic; /* probably will support different formats */

__le16 eh_entries; /* number of valid entries */

__le16 eh_max; /* capacity of store in entries */

__le16 eh_depth; /* has tree real underlying blocks? */

__le32 eh_generation; /* generation of the tree */

};


하나의 extent는 연속된 physical block을 나타낸다.

ee_start_hi / ee_start_lo

start block index. ext4는 48bit block index를 사용하고 있다는 것을 상기하자.

sizeof(ee_start_hi) == 2, sizeof(ee_start_lo) == 4

ee_len

length of continous block. __le16 type이므로, 최대 32768개의 연속 block을 나타낼 수 있고, 4KB block size일 경우, 최대 4K * 32768 = 128M 를 표시할 수 있다.

eh_max

현재 block에서 가질 수 있는 max entry 수. entry 수가 이 값을 넘어가면 extent/index 를 split해야 한다.

size = (inode->i_sb->s_blocksize - sizeof(struct ext4_extent_header))

/ sizeof(struct ext4_extent);

--- or ---

size = sizeof(EXT4_I(inode)->i_data);

size -= sizeof(struct ext4_extent_header);

size /= sizeof(struct ext4_extent_idx);

--- or ---

size = sizeof(EXT4_I(inode)->i_data);

size -= sizeof(struct ext4_extent_header);

size /= sizeof(struct ext4_extent);


One extent represents continuous physical blocks.

ee_start_hi / ee_start_lo

start block index. remind that ext4 uses 48bit for block index.

sizeof(ee_start_hi) == 2, sizeof(ee_start_lo) == 4

ee_len

length of continuous block. It's type is __le16. Therefore it can represent maximum 32768 continuous blocks. And in case that block size is 4KB, it can represent maximum 4K * 32768 = 128MB.

eh_max

maximum number of entries in current block. extent/index should be splitted if number of entries exceeded this value.



따라서, 4KB block size를 가정하면, ext4_inode의 i_block array는

최악의 경우 : continuous한 block이 없을 경우 (즉 하나의 extent가 오직 한 block만 가리킨다. ee_len = 1)

4K * 4 = 16KB

최상의 겨우 : ee_len = 32768

128M * 4 = 512M

까지 나타낼 수 있다.
So, assuming 4KB block size, i_block array of ext4_inode can represent
    worst case : There is no continuous block. (That is, one extent can indicate only one block. ee_len = 1)
       
4K * 4 = 16KB
    best case : ee_len = 32768 

128M * 4 = 512M


이 한계를 극복하기 위해서 HTree방식의 extent tree가 사용된다.

아래는 ext4의 extent tree의 layout이다.(출처 : ols2007v2-pages-21-34.pdf from kernel.org)
To win over this limitation, HTree algorithm is used for extent tree.
Following picture is layout of ext4 extent tree.(The source : 
ols2007v2-pages-21-34.pdf from kernel.org)
(http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&sqi=2&ved=0CEwQFjAA&url=http%3A%2F%2Fkernel.org%2Fdoc%2Fols%2F2007%2Fols2007v2-pages-21-34.pdf&ei=PLHWT_rUOayUiAew86mSAw&usg=AFQjCNF8Yzcg4Z5VRsEGWLMCEWNEJGx2sw&sig2=SDilL5_ETwCMRA9--mQ8lA)

이런 방식의 위의 한계를 극복하고 있다.

This is the way to recover above limitation.


inode의 extent는 60 bytes만 사용가능하지만, leaf nodes의 extent의 경우는 한 block전체를 사용하므로, 처음 12 bytes header를 제외하면, 그 뒤는 쭉~ "struct ext4_extent"의 array이다. array의 크기는 header->eh_entries에 저장되어 있다.

따라서, visit algorithm은
Only 60 bytes are available for inode extent. But, one entire block can be used for leaf node extent. Therefore, excluding first 12 bytes header, all are array of struct ext4_extent.
Length of array is stored at header->eh_entries.
Therefore, visiting algorithm is


struct ext4_extent_header *eh = (struct ext4_extent_header *)block;

struct ext4_extent         *e = (struct ext4_extent *)((char *)block + sizeof(*eh))

struct ext4_extent      *eend = e + eh->eh_entries;

while (e < eend) {

... do something ..

e++;

}


index node block의 경우 leaf node와 마찬가지로 12 bytes header가 처음에 오고, 그 뒤로 "struct ext4_extent_idx"의 array가 나타난다.

따라서 visit algorithm은
In case of index node block, like leaf node, 12 bytes header follows by array of struct ext4_extent_idx.
Therefore visiting algorithm is


struct ext4_extent_header *eh = (struct ext4_extent_header *)block;

struct ext4_extent_idx    *ei = (struct ext4_extent_idx *)((char *)block + sizeof(*eh))

struct ext4_extent     *eiend = ei + eh->eh_entries;

while (ei < eiend) {

... do something ..

/* "(ei->ei_leaf_hi << 32) | ei->ei_leaf_lo" 는 child block을 가리킨다. */
/* ei->ei_leaf_hi << 32 | ei->ei_leaf_lo means child block. */

ei++;

}


그렇다면, 현재 extent block이 leaf node인지 아니면 index node인지는 어떻게 판단할 수 있는가?
Then, how can we know a certain extent block is whether leaf node or index node?


if "header->eh_depth > 0" than "index node"

else if "header->eh_depth == 0" than "leaf node"


따라서, extent tree를 따라가는 algorithm은
So, algorithm for visiting extent tree is


struct ext4_extent_header *eh = (struct ext4_extent_header *)block;

if (eh->depth)

do_index_node(eh, block)

else

do_leaf_node(eh, block)


본격적으로 directory의 구조로 들어가 보자 (extent를 사용하는 경우를 가정하자.)

directory 역시 file이므로 위의 기본 구조를 따른다.

file의 block은 file의 content를 담고 있고, directory 역시 directory content를 담고 있는데, 다음과 같은 format을 따른다.
Now it's time for talking about directory structure (assuming the case using extent).
directory is also file. So, basic structure is same with the one we mentioned so far.
file block has file content, and directory also has directory content. Format is like below.


Structure across 2 blocks

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


|<------------------------- block 0 ------------------------->|<------------------ block 1--------------------->|

+----+-----------+--------+-----------------------------------+-----------+--------+----------------------------+

|... | struct DE | <name> |  padding                          | struct DE | <name> | ...                        |

+----+-----------+--------+-----------------------------------+-----------+--------+----------------------------+

|... | rec_len = 8 + name + 4 byte align + "len of padding"   |           same as normal (see above)            |

+----+-----------+--------+-----------------------------------+-------------------------------------------------+


< NOTE : 여기서 '8'은 'name' field를 제외한 sizeof(struct ext4_dir_entry_2)를 나타낸다.) >
< NOTE : 8 means sizeof(struct ext4_dir_entry_2) excluding name field. >


대부분의 경우 struct DE == struct ext4_dir_entry_2 를 나타낸다.
Most cases, struct DE means struct ext4_dir_entry_2.


#define EXT4_NAME_LEN 255


struct ext4_dir_entry_2 {

 __le32 inode;                 /* Inode number */

 __le16 rec_len;               /* Directory entry length */

 __u8 name_len;                /* Name length */

 __u8 file_type;

 char name[EXT4_NAME_LEN];     /* File name */

};


위에서 보듯이 ext4에서 file name의 최대 길이는 255이다.
As you can see above, maximum length of file name at ext4 is 255.

즉, directory block의 경우, struct ext4_dir_entry_2 구조가 계속해서 저장되는데, 현재 DE(directory entry)의 위치에서 'rec_len'만큼 더하면, 다음 DE로 갈 수 있다.
따라서, block의 마지막 DE의 rec_len는 항상 "현재 block의 마지막 / 다음 block의 시작" 을 가리킨다.
In case of directory block, ext4_dir_entry_2 structures are stored in sequence. So, we can move to next DE(directory entry) by adding rec_len to current DE.
The last DE of a certain block always means "end of current block / begin of next block".

한가지 주목할 사항은 'inode' 값에 대한 내용인데, '0'은 invalid한 directory entry를 뜻한다.
특히 delete algorithm 을 살펴보면 (For details : ext4_delete_entry() at ext4/namei.c from kernel)
One important thing to know is meaning of inode value. 0 inode value means invalid directory entry.
Especially, you can observe that below algorithm is used to delete file (for details : ext4_delete_entry() at ext4/namei.c from kernel)

< block 중간의 entry - DE1 - 를 삭제하는 경우 >
< Case : Entry in the middle of block - DE1 - is deleted >
+-----+-----+-----+-----+       +-----+-----------+-----+
| ... | DE0 | DE1 | ... |  ==>  | ... |    DE0    | ... |
+-----+-----+-----+-----+       +-----+-----------+-----+
--> 이 경우, DE0->rec_len += DE1->rec_len 가 된다.
--> In this case, DE0->rec_len += DE1->rec_len.

< block의 첫번재 entry - DE0 - 를 삭제하는 경우 >
< Case : The first entry of block - DE0 - is deleted >
+-----+-----+       +---------------+-----+
| DE0 | ... |  ==>  | DE0 <invalid> | ... |
+-----+-----+       +---------------+-----+
--> "DE0->inode = 0" 을 통해서 DE0를 invalid entry로 mark한다.
--> DE0 is marked as invalid by "DE0->inode = 0" statement.

정리해 보면, directory entry를 읽는 대략적인 알고리즘은 다음과 같다.
In summary, rough algorithm for reading directory is
(For details : ext_readdir() at ext4/dir.c from kernel)

struct ext4_dir_entry_2 *de = (struct ext4_dir_entry_2 *)block;
struct ext4_dir_entry_2 *deend = block + block_size; /* block size is usually 4KB */
while (de < deend) {
if (de->inode) { /* check that this is valid or not */
... do something ...
}
de = (struct ext4_dir_entry_2 *)((char*)de + de->rec_len);
}


.... 음 .... 일단 여기까지... 나중에 좀더 정리해야겠당....
... to be continued...

소프트웨어 엔지니어로 10년 가량 직장생활을 하면서 R&R(Role And Responsibility)에 대한 고민은 항상 있어 왔던 것 같다.
블로그에 R&R에대한 다른 포스트들도 여럿 더 있고...
뭐, 사실 이런 류의 논의는 정답이 없기 때문에 더욱 곤란한 경우가 많은데, 각설하고, 한가지 경우에 대한 장단점을 이야기 하고자 한다.

R&R에 대해 이야기 할때, 가장 주로 사용되는 방식이...

너의(혹은 너의 팀의) 주 임무(Main Role)은 'xxx'이다. 그렇지만, 거기에 국한되지는 않는다(not limited). 그러므로 경우에 따라서는 다른 사람(팀)의 일을 도와 주어야 하는 경우도 있을 것이다.


이런 식인데... 과연 이게 적절한 방식인가에 대한 이야기이다

물론, 일 자체가 너무 달라서, 서로의 영역을 서로 도와 줄 수 없는 경우는 고민할 거리도 없지만... 개발 조직 내에서는, 특히 소프트웨어 에서는, 다른 영역에 contribution하는게 충분히 가능하기 때문에 위와 같은 정의가 자주 사용되는 것 같다.


사실 "너는 이 일만 해!"는 현실적으로 불가능하다. 그래서 "거기에 국한되지는 않는다.(not limited)"라는 말이 항상 들어가게 된다.

왜 현실적으로 불가능한가?

일이라는게, 항상 고정되어 있는 것도 아닐 뿐더러, 어떤 때는 이 일이 많았다가, 어떤 때는 저 일이 많았다가... 이런 식이기 때문이다.


장점이라면, 당연히, 남는 자원을 적절히 분배할 수 있다. 특정 일이 많을때는 그 일을 서로 나누어서 할 수 있으니, 인적자원의 효율적인 활용이 가능해 진다.

단점이라면? 사실 이 부분이 이 글에서 이야기하고 싶은 부분인데... 예를 들어 A, B 두 팀이 있다고 생각해 보자.

A팀은 굉장히 적극적이고 또 능력도 뛰어나서 맡은 일을 빠른 속도로 처리해 나가는 팀이고. B팀은 A팀과 반대되는 성향을 가진다고 생각하자.

이 경우, 위와 같은 R&R의 정의를 적용하면, B팀은 항상 제 시간에 일 처리를 끝내지 못하기 때문에 A팀은 항상 B팀을 도와주는 형태로 업무가 진행되게 된다.

A팀은, 비록, 열심히 부지런히, 그리고 소위 '스마트'한 방법으로 자신들의 역할을 다 했음에도 불구하고, B팀의 상황이 안 좋기 때문에 B팀을 도와 주게 되는 것이다.

만약 B팀 역시 A팀과 비등한 정도의 열정/성실/능력 을 가진 팀이라면, 이런 상황은 어쩔 수 없고, A팀 역시 커다른 불만 없이 B틈을 도와 줄 것이다.

왜냐하면, 이 경우는 명백하게, 순간적으로 B팀에게 일이 몰리는 상황이고 이것은 어떻게 처리할 수 없는 형국이기 때문이다.


그렇지만 많은 경우, B팀의 불성실/게으름 등등의 이유로 절대적인 일의 양은 A팀에 비해 많지 않았음에도(혹은 오히려 적었음에도) 불구하고 이런 일이 일어나게 된다. 그리고 이런 경우, A팀에서 이런 사실을 모를리 없다. 이는 당연히 불만으로 이어지고, A팀의 사기를 떨어뜨리게 된다. A팀 역시 일을 부지런히, 빨리 끝낼 이유가 없으니까... 빨리 끝내봤자 B팀의 일을 해야 하니...

B팀 역시, 어차피 A팀이 도와줄테니, 최선을 다해서 현 상황을 이겨나갈려고 하지도 않는다.

즉, B팀 입장에서는 Role은 있으되, Responsibility는 없는 형국이랄까... (A, B팀 공동 책임이다.)


자... 이 두 가지 장/단점 중에서 어느쪽에 손을 들어주어야 하는가?

물론, 항상 어느 한 팀의 일이 많다면, 이것은 일의 양을 잘못 판단한 관리자의 책임이고, 팀 구성을 새롭게 가져 가야 한다.


개인적으로는 지금까지, 위와 같은 R&R의 정의만 보아 왔다. 따라서, 위와 반대되는 R&R을 정의하고 조직이 흘러가는 경향을 관찰해 보고 싶은 호기심을 느낀다.

긍정적인 방향이라면, 각 팀의 자신의 일을 최대한 효율적을 빨리 끝내려고 '스마트'한 일 처리 방식을 끊임없이 연구할 것이고, 일의 전반적인 효율이 증대될 것이다.(일을 빨리 끝내면, 놀 수 있으므로...)

부정적인 방향은, "왜 우리만 항상 고생하는데!"와 같은 불만으로 팀웤이 손상되는 방향이 있을 수 있겠다.


일의 영역이 장기간 고정적이고 변화가 적다면, 유연성이 없는 엄격한 R&R정의도 고민해 볼 필요가 있지 않을까?


+ Recent posts