Surface Flinger 란 무엇인가?
frame buffer HAL 은 디바이스 드라이버에서 올라가다가 마지막에 Surface Flinger 와 연결된다.
즉 Surface Flinger 는 frame buffer HAL 위에있는 또다른 추상화 계층이라고 볼 수 있다.
무엇을 추상화 하는지 알아보도록 하자.


Surface Flinger 소스분석
먼저 "frameworks/base/libs/surfaceflinger/SurfaceFlinger.h" 파일을 시작점으로 하자.
일단 헤더에 정의된 클래스들과 멤버변수 부터 보자.

class Client : public RefBase {

public:

    SharedClient*           ctrlblk;

    ClientID                cid;


private:

    int                             mPid;

    uint32_t                        mBitmap;

    SortedVector<uint8_t>           mInUse;

    Vector< wp<LayerBaseClient> >   mLayers;

    sp<IMemoryHeap>                 mCblkHeap;

    sp<SurfaceFlinger>              mFlinger;

}

class GraphicsPlane {

private:

        DisplayHardware*        mHw;

        Transform               mTransform;

        Transform               mOrientationTransform;

        Transform               mGlobalTransform;

        int                     mOrientation;

}


class SurfaceFlinger : public BnSurfaceComposer, protected Thread {

private:

    mutable     MessageQueue    mEventQueue;

    mutable     Mutex                   mStateLock;

                State                   mCurrentState;

                State                   mDrawingState;

    volatile    int32_t                 mTransactionFlags;

    volatile    int32_t                 mTransactionCount;

                Condition               mTransactionCV;

                bool                    mResizeTransationPending;

                Tokenizer                               mTokens;

                DefaultKeyedVector<ClientID, sp<Client> >   mClientsMap;

                DefaultKeyedVector<SurfaceID, sp<LayerBaseClient> >   mLayerMap;

                GraphicPlane                            mGraphicPlanes[1];

                bool                                    mLayersRemoved;

                Vector< sp<Client> >                    mDisconnectedClients;

                sp<IMemoryHeap>             mServerHeap;

                surface_flinger_cblk_t*     mServerCblk;

                GLuint                      mWormholeTexName;

                nsecs_t                     mBootTime;

                Permission                  mHardwareTest;

                Permission                  mAccessSurfaceFlinger;

                Permission                  mDump;

                Region                      mDirtyRegion;
                Region                      mDirtyRegionRemovedLayer;
                Region                      mInvalidRegion;
                Region                      mWormholeRegion;
                bool                        mVisibleRegionsDirty;
                bool                        mDeferReleaseConsole;
                bool                        mFreezeDisplay;
                int32_t                     mFreezeCount;
                nsecs_t                     mFreezeDisplayTime;
                int                         mDebugRegion;
                int                         mDebugBackground;
                volatile nsecs_t            mDebugInSwapBuffers;
                nsecs_t                     mLastSwapBufferTime;
                volatile nsecs_t            mDebugInTransaction;
                nsecs_t                     mLastTransactionTime;
                bool                        mBootFinished;
    mutable     Barrier                     mReadyToRunBarrier;
                enum {
                    eConsoleReleased = 1,
                    eConsoleAcquired = 2
                };
   volatile     int32_t                     mConsoleSignals;
   volatile     int32_t                     mSecureFrameBuffer;

class SurfaceFlinger::LayerVector {
        private:
           KeyedVector< sp<LayerBase> , size_t> lookup;
           Vector< sp<LayerBase> >              layers;
        }

}


class FreezeLock {

private:

    SurfaceFlinger* mFlinger;

}


class BClient : public BnSurfaceFlingerClient {

private:

    ClientID            mId;

    SurfaceFlinger*     mFlinger;

    sp<IMemoryHeap>     mCblk;

}

일단 여기서 보이는 것은 SurfaceFlinger 가 LayerVector 를 가지고 있다는 것이다.
이것만 놓고 생각했을때에는 SurfaceFlinger 가 Layer 들을 관리하고 Client 가 생성될때 마다 레이어를 만들어서 나눠줄꺼 같다.

그럼 클라이언트 소스를 약간 보자.

Client::Client(ClientID clientID, const sp<SurfaceFlinger>& flinger)

    : ctrlblk(0), cid(clientID), mPid(0), mBitmap(0), mFlinger(flinger)      

{

    const int pgsize = getpagesize();                                        

    const int cblksize = ((sizeof(SharedClient)+(pgsize-1))&~(pgsize-1));    


    mCblkHeap = new MemoryHeapBase(cblksize, 0,

            "SurfaceFlinger Client control-block");                          


    ctrlblk = static_cast<SharedClient *>(mCblkHeap->getBase());              

    if (ctrlblk) { // construct the shared structure in-place.                

        new(ctrlblk) SharedClient;      

    }

}

status_t Client::bindLayer(const sp<LayerBaseClient>& layer, int32_t id)

{

    ssize_t idx = mInUse.indexOf(id);

    if (idx < 0)

        return NAME_NOT_FOUND;

    return mLayers.insertAt(layer, idx);

}

sp<LayerBaseClient> Client::getLayerUser(int32_t i) const {

    sp<LayerBaseClient> lbc;

    ssize_t idx = mInUse.indexOf(uint8_t(i));

    if (idx >= 0) {

        lbc = mLayers[idx].promote();

        LOGE_IF(lbc==0, "getLayerUser(i=%d), idx=%d is dead", int(i), int(idx));

    }

    return lbc;

}

소스를 보면 Client 에는 레이어를 생성하는 부분은 없다.
결국 레이어는 SurfaceFlinger 에서만 관리하는게 맞는거 같다.
또한 Client 는 레이어를 여러개 가질수 있게 되어있다.
bindLayer 함수와 getLayerUser 함수를 보면 알 수 있다.

이것으로 Client 는 SurfaceFlinger 에서 생성되어 사용된다고 판단된다.
그럼 SurfaceFlinger 함수를 약간 보자.

sp<ISurfaceFlingerClient> SurfaceFlinger::createConnection()

{

    Mutex::Autolock _l(mStateLock);

    uint32_t token = mTokens.acquire();


    sp<Client> client = new Client(token, this);

    if (client->ctrlblk == 0) {

        mTokens.release(token);

        return 0;

    }

    status_t err = mClientsMap.add(token, client);

    if (err < 0) {

        mTokens.release(token);

        return 0;

    }

    sp<BClient> bclient = new BClient(this, token, client->getControlBlockMemory());

    return bclient;

}

void SurfaceFlinger::destroyConnection(ClientID cid)
{
    Mutex::Autolock _l(mStateLock);
    sp<Client> client = mClientsMap.valueFor(cid);
    if (client != 0) {
        Vector< wp<LayerBaseClient> > layers(client->getLayers());
        const size_t count = layers.size();
        for (size_t i=0 ; i<count ; i++) {
            sp<LayerBaseClient> layer(layers[i].promote());
            if (layer != 0) {
                purgatorizeLayer_l(layer);
            }
        }

        mClientsMap.removeItem(cid);
        mDisconnectedClients.add(client);
        setTransactionFlags(eTransactionNeeded);
    }
}
SurfaceFlinger 에서 createConnection 과 destroyConnection 으로 Client 를 생성하고 삭제한다.
그런데 왜하필 connection 이라고 썻을까? createClient 라고 하면 안되었을까?
이것은 아마 웹서버와 비슷한 구조로 보인다.
예를들어 SurfaceFlinger 를 하나의 웹서버 라고 보면 Client 는 웹서버에 접속한 하나의 클라이언트 인 것이다. 그래서 connection 이라고 표현되어 있는것이다.

그리고 여기에 BClient 라는 클래스가 생성된다. 소스에서 보면 Client 에서 생성한 ControlBlockMemory 를 얻어와서 BClient 생성하는데 쓰는것을 볼 수 있다.
그럼 소스를 한번 보자.

 BClient::BClient(SurfaceFlinger *flinger, ClientID cid, const sp<IMemoryHeap>& cblk)

    : mId(cid), mFlinger(flinger), mCblk(cblk)

{

}


BClient::~BClient() {

    // destroy all resources attached to this client

    mFlinger->destroyConnection(mId);

}


sp<IMemoryHeap> BClient::getControlBlock() const {

    return mCblk;

}


sp<ISurface> BClient::createSurface(

        ISurfaceFlingerClient::surface_data_t* params, int pid,

        DisplayID display, uint32_t w, uint32_t h, PixelFormat format,

        uint32_t flags)

{

    return mFlinger->createSurface(mId, pid, params, display, w, h, format, flags);

}


status_t BClient::destroySurface(SurfaceID sid)

{

    sid |= (mId << 16); // add the client-part to id

    return mFlinger->removeSurface(sid);

}


status_t BClient::setState(int32_t count, const layer_state_t* states)

{

    return mFlinger->setClientState(mId, count, states);

}

소스가 상당히 짧은데 사실 여기서 하는일은 거의 없다. 그저 SurfaceFlinger 의 함수를 호출하는 일 밖에 없다.
그런데 이 함수를 만든 이유는 무엇일까? 그것은 바로 바인더 통신으로 넘기기 위해서이다.
바인더에 대해선 다음에 알아보자.

그럼 이제 레이어 에 대해서 알아보자
레이어는 client 의 bindLayer 함수에 의해 클라이언트에 추가된다.
이 것을 행하는 소스는 아마 SurfaceFlinger 에 있을것으로 예상되니 SurfaceFlinger.cpp 파일을 열어서 찾아보자.

 sp<ISurface> SurfaceFlinger::createSurface(ClientID clientId, int pid,

        ISurfaceFlingerClient::surface_data_t* params,

        DisplayID d, uint32_t w, uint32_t h, PixelFormat format,

        uint32_t flags)

{

    sp<LayerBaseClient> layer;
sp<LayerBaseClient::Surface> surfaceHandle;

...


    switch (flags & eFXSurfaceMask) {
        case eFXSurfaceNormal:
            if (UNLIKELY(flags & ePushBuffers)) {
                layer = createPushBuffersSurfaceLocked(client, d, id,  w, h, flags);
            } else {
                layer = createNormalSurfaceLocked(client, d, id,  w, h, flags, format);
            }
            break;
        case eFXSurfaceBlur:
            layer = createBlurSurfaceLocked(client, d, id, w, h, flags);
            break;
        case eFXSurfaceDim:
            layer = createDimSurfaceLocked(client, d, id, w, h, flags);
            break;
    }

    if (layer != 0) {
        setTransactionFlags(eTransactionNeeded);
        surfaceHandle = layer->getSurface();
        if (surfaceHandle != 0) {
            params->token = surfaceHandle->getToken();
            params->identity = surfaceHandle->getIdentity();
            params->width = w;
            params->height = h;
            params->format = format;
        }
    }

    return surfaceHandle;
}

찾아보았으나 bindLayer 는 SurfaceFlinger 에서 호출하지 않는다.
대신 createSurface 함수에서 Layer 를 만들도록 하였다.
만드는 함수가 createXXXXSurfaceLocked 로 되어있으니 그중 가장 Normal 한것을 열어보도록 하자.

 sp<LayerBaseClient> SurfaceFlinger::createNormalSurfaceLocked(

        const sp<Client>& client, DisplayID display,

        int32_t id, uint32_t w, uint32_t h, uint32_t flags,

        PixelFormat& format)

{

    // initialize the surfaces

    switch (format) { // TODO: take h/w into account

    case PIXEL_FORMAT_TRANSPARENT:

    case PIXEL_FORMAT_TRANSLUCENT:

        format = PIXEL_FORMAT_RGBA_8888;

        break;

    case PIXEL_FORMAT_OPAQUE:

        format = PIXEL_FORMAT_RGB_565;

        break;

    }


    sp<Layer> layer = new Layer(this, display, client, id);

    status_t err = layer->setBuffers(w, h, format, flags);

    if (LIKELY(err == NO_ERROR)) {

        layer->initStates(w, h, flags);

        addLayer_l(layer);

    } else {

        LOGE("createNormalSurfaceLocked() failed (%s)", strerror(-err));

        layer.clear();

    }

    return layer;

}


여기서 레이어를 생성한다.
그럼 먼저 Layer.h 파일을 확인하자.

class Layer : public LayerBaseClient {

private:

    sp<Surface>             mSurface;

            bool            mSecure;

            bool            mNoEGLImageForSwBuffers;

            int32_t         mFrontBufferIndex;

            bool            mNeedsBlending;

            bool            mNeedsDithering;

            Region          mPostedDirtyRegion;

            sp<FreezeLock>  mFreezeLock;

            PixelFormat     mFormat;


            // protected by mLock

            sp<GraphicBuffer> mBuffers[NUM_BUFFERS];

            Texture         mTextures[NUM_BUFFERS];

            sp<GraphicBuffer> mHybridBuffer;

            uint32_t        mWidth;

            uint32_t        mHeight;

   mutable Mutex mLock;

}

그럼 Layer 가 초기화 되는부분을 보자.
Layer.cpp 파일을 열어보면 아래와 같은 부분이 있다.

 Layer::Layer(SurfaceFlinger* flinger, DisplayID display,

        const sp<Client>& c, int32_t i)

    :   LayerBaseClient(flinger, display, c, i),

        mSecure(false),

        mNoEGLImageForSwBuffers(false),

        mNeedsBlending(true),

        mNeedsDithering(false)

{

    // no OpenGL operation is possible here, since we might not be

    // in the OpenGL thread.

    mFrontBufferIndex = lcblk->getFrontBuffer();

}

과연 lcbk 가 무엇일까?
Layer.h 와 Layer.cpp 에 없다. 그렇다면 상속받은 상위 클래스에 있다는 얘기니까 상위 클래스를 확인해 보자.
먼저 LayerBase.h 파일을 열어보자.

class LayerBase : public RefBase {

public:
    static const uint32_t typeInfo;
    static const char* const typeID;
    DisplayID           dpy;
    mutable bool        contentDirty;
            Region      visibleRegionScreen;
            Region      transparentRegionScreen;
            Region      coveredRegionScreen;

            struct State {
                uint32_t        w;
                uint32_t        h;
                uint32_t        requested_w;
                uint32_t        requested_h;
                uint32_t        z;
                uint8_t         alpha;
                uint8_t         flags;
                uint8_t         reserved[2];
                int32_t         sequence;   // changes when visible regions can change
                uint32_t        tint;
                Transform       transform;
                Region          transparentRegion;
            };
protected:
                sp<SurfaceFlinger> mFlinger;
                uint32_t        mFlags;

                // cached during validateVisibility()
                bool            mTransformed;
                bool            mUseLinearFiltering;
                int32_t         mOrientation;
                GLfixed         mVertices[4][2];
                Rect            mTransformedBounds;
                int             mLeft;
                int             mTop;

                // these are protected by an external lock
                State           mCurrentState;
                State           mDrawingState;
    volatile    int32_t         mTransactionFlags;

                // don't change, don't need a lock
                bool            mPremultipliedAlpha;

                // atomic
    volatile    int32_t         mInvalidate;

}

class LayerBaseClient : public LayerBase {

public:

    SharedBufferServer*     lcblk;


private:

                int32_t         mIndex;

    mutable     Mutex           mLock;

    mutable     wp<Surface>     mClientSurface;

    const       uint32_t        mIdentity;

    static      int32_t         sIdentity;

}

LayerBaseClient 함수에서 SharedBufferServer* 형태로 선언되어 있는것을 알 수있다.

그럼 저 변수가 생성되는 부분을 찾아보자.

LayerBase.cpp 파일을 열면 아래 부분이 있다.

 LayerBaseClient::LayerBaseClient(SurfaceFlinger* flinger, DisplayID display,

        const sp<Client>& client, int32_t i)

    : LayerBase(flinger, display), lcblk(NULL), client(client),

      mIndex(i), mIdentity(uint32_t(android_atomic_inc(&sIdentity)))

{

    lcblk = new SharedBufferServer(

            client->ctrlblk, i, NUM_BUFFERS,

            mIdentity);

}

여기서 lcblk 변수를 생성하는데 생성자로 client->ctrlblk 가 들어간다.

이것은 Client 에서 생성된 메모리인데 아마 이 클래스는 고유의 메모리를 같는것이 아니라 Client 에서 생성된 메모리로 서버 역할을 하는듯 하다.

이부분은 메모리 부분이라 나중에 분석하도록 하겠다.


이제 lcblk 가 어디서 생성되는지 알았으니 다시 Layer 로 돌아가자.

레이어는 생성되고나서 setBuffers 함수가 호출되는것을 볼 수있다.

Layer.cpp 파일을 열어서 확인해 보자.

 status_t Layer::setBuffers( uint32_t w, uint32_t h,

                            PixelFormat format, uint32_t flags)

{

    // this surfaces pixel format

    PixelFormatInfo info;

    status_t err = getPixelFormatInfo(format, &info);

    if (err) return err;


    // the display's pixel format

    const DisplayHardware& hw(graphicPlane(0).displayHardware());

    PixelFormatInfo displayInfo;

    getPixelFormatInfo(hw.getFormat(), &displayInfo);

    const uint32_t hwFlags = hw.getFlags();


    mFormat = format;

    mWidth = w;

    mHeight = h;

    mSecure = (flags & ISurfaceComposer::eSecure) ? true : false;

    mNeedsBlending = (info.h_alpha - info.l_alpha) > 0;

    mNoEGLImageForSwBuffers = !(hwFlags & DisplayHardware::CACHED_BUFFERS);


    // we use the red index

    int displayRedSize = displayInfo.getSize(PixelFormatInfo::INDEX_RED);

    int layerRedsize = info.getSize(PixelFormatInfo::INDEX_RED);

    mNeedsDithering = layerRedsize > displayRedSize;


    for (size_t i=0 ; i<NUM_BUFFERS ; i++) {

        mBuffers[i] = new GraphicBuffer();

    }

    mSurface = new SurfaceLayer(mFlinger, clientIndex(), this);

    return NO_ERROR;

}

일단 mBuffers 에다가 GraphicsBuffer 를 생성해서 채우는데 이게 무엇일까?

include 를 보니까 "ui/GraphicsBuffer.h" 파일에서 가져오는거 같다. 일단은 현 디렉토리만 분석중이니 넘어가자.

libs 디렉토리와 ui 디렉토리로 나누어 놓은 이유도 있을꺼 같은데 아직은 이유가 무엇인지 알수없다.


그럼 SurfaceLayer 소스를 보기위해 Layer.cpp 파일을 열어서 아래 부분을 찾아보자.

Layer::SurfaceLayer::SurfaceLayer(const sp<SurfaceFlinger>& flinger,

        SurfaceID id, const sp<Layer>& owner)

    : Surface(flinger, id, owner->getIdentity(), owner)

{

}


Layer::SurfaceLayer::~SurfaceLayer()

{

}

하는일이 아무것도 없다.

결국 그냥 Surface 를 상속받은 껍데기일 뿐이다.

Surface 역시 ui 에 있는데 이제 ui 쪽을 봐야할꺼 같다.


그럼 일단 Surface 를 찾아보도록 하자.

ui 쪽은 libs 쪽처럼 헤더가 붙어있는것이 아니고 나누어져 있다.

헤더는 frameworks/base/include/ui 에 있고 소스는 frameworks/base/libs/ui 에 있다.


나누어져 있다는것은 header 를 여기저기서 가져다 쓸수 있게 만들어둔다는 소리다.

그렇다면 ui 는 윗단에서 가져다 쓸 수 있게 만든것이고 libs 는 그 자체로만 쓸수 있게 만들어 놓은거 같다.


그럼 먼저 Surface.h 파일을 열어보자.

class SurfaceControl : public RefBase {

private:

    sp<SurfaceComposerClient>   mClient;

    sp<ISurface>                mSurface;

    SurfaceID                   mToken;

    uint32_t                    mIdentity;

    uint32_t                    mWidth;

    uint32_t                    mHeight;

    PixelFormat                 mFormat;

    uint32_t                    mFlags;

    mutable Mutex               mLock;

    mutable sp<Surface>         mSurfaceData;

}


class Surface : public EGLNativeBase<android_native_window_t, Surface, RefBase> {

private:

    // constants
    sp<SurfaceComposerClient>   mClient;
    sp<ISurface>                mSurface;
    SurfaceID                   mToken;
    uint32_t                    mIdentity;
    PixelFormat                 mFormat;
    uint32_t                    mFlags;
    GraphicBufferMapper&        mBufferMapper;
    SharedBufferClient*         mSharedBufferClient;

    // protected by mSurfaceLock
    Rect                        mSwapRectangle;
    uint32_t                    mUsage;

    // protected by mSurfaceLock. These are also used from lock/unlock
    // but in that case, they must be called form the same thread.
    sp<GraphicBuffer>           mBuffers[2];
    mutable Region              mDirtyRegion;

    // must be used from the lock/unlock thread
    sp<GraphicBuffer>           mLockedBuffer;
    sp<GraphicBuffer>           mPostedBuffer;
    mutable Region              mOldDirtyRegion;
    bool                        mNeedFullUpdate;

    // query() must be called from dequeueBuffer() thread
    uint32_t                    mWidth;
    uint32_t                    mHeight;

    // Inherently thread-safe
    mutable Mutex               mSurfaceLock;
    mutable Mutex               mApiLock;

}

 

그럼 Surface.cpp 파일도 열어보자.

Surface::Surface(const sp<SurfaceControl>& surface)
    : mClient(surface->mClient), mSurface(surface->mSurface),
      mToken(surface->mToken), mIdentity(surface->mIdentity),
      mFormat(surface->mFormat), mFlags(surface->mFlags),
      mBufferMapper(GraphicBufferMapper::get()), mSharedBufferClient(NULL),
      mWidth(surface->mWidth), mHeight(surface->mHeight)
{
    mSharedBufferClient = new SharedBufferClient(
            mClient->mControl, mToken, 2, mIdentity);

    init();
}

 void Surface::init()
{
    android_native_window_t::setSwapInterval  = setSwapInterval;
    android_native_window_t::dequeueBuffer    = dequeueBuffer;
    android_native_window_t::lockBuffer       = lockBuffer;
    android_native_window_t::queueBuffer      = queueBuffer;
    android_native_window_t::query            = query;
    android_native_window_t::perform          = perform;
    mSwapRectangle.makeInvalid();
    DisplayInfo dinfo;
    SurfaceComposerClient::getDisplayInfo(0, &dinfo);
    const_cast<float&>(android_native_window_t::xdpi) = dinfo.xdpi;
    const_cast<float&>(android_native_window_t::ydpi) = dinfo.ydpi;
    // FIXME: set real values here
    const_cast<int&>(android_native_window_t::minSwapInterval) = 1;
    const_cast<int&>(android_native_window_t::maxSwapInterval) = 1;
    const_cast<uint32_t&>(android_native_window_t::flags) = 0;
    // be default we request a hardware surface
    mUsage = GRALLOC_USAGE_HW_RENDER;
    mNeedFullUpdate = false;
}

Surface 클래스는 android_native_window_t 구조체를 상속받고 있기 때문에 init 함수에서 해당 구조체를 채워주는 일을 한다.
구조체로 상속할수 있다는것을 첨알았다.

자 이제 더이상 따라갈 수가 없다.
이부분에 대해서는 조금 더 생각해 봐야겠다.


hello 디바이스 만들기
프로그래밍 첨하면 아무나 다하는 hello.c를 만들어 보겠다.

#include <linux/init.h>

#include <linux/module.h>

MODULE_LICENSE("Dual BSD/GPL");


static int __init hello_init(void) {

        printk(KERN_ALERT "Hello, world\n");

        return 0;

}

static void __exit hello_exit(void) {

        printk(KERN_ALERT "Goodbye, world\n");

}

module_init(hello_init);

module_exit(hello_exit);

뭐하는 설명 안해도 다 알만한 프로그램이다.
여기서 기존의 프로그램과 다른점은 printf 대신 printk 를 썻다는 것이다.
저 코드들은 커널 에서 실행되기때문에 유저스페이스의 함수를 사용할 수 없다.
그래서 stdio 같은것들을 인클루드 하면 안된다. 모두 커널상의 함수를 사용해야한다.
printk 역시 커널 함수로 printf와 거의 유사한 기능을 한다.

중간에 __init 과 __exit 가 좀 수상쩍어 보인다.
이것은 필수는 아니지만 써주는게 좋다. 그 이유는 커널에게 시작할때 한번 끝날때 한번 실행하는 함수라고 알려서 실행되고 바로 메모리에서 삭제하도록 할 수있다.

MODULE_LICENSE 는 라이센스를 지정하는 부분인데 이부분이 재미있다.
여기에 들어갈 수 있는 스트링은 아래와 같다.

"GPL"  ,  "GPL v2"  ,  "GPL and additional rights"  ,  "Dual BSD/GPL"  ,  "Dual MPL/GPL"

"Proprietary"

 사실 MODULE_LICENSE 는 옵션이다. 그러나 지정하지 않은 모듈을 적재시 "오염 상태" 가 된다.

makefile 만들기

KERNELDIR ?= /lib/modules/$(shell uname -r)/build

PWD := $(shell pwd)

obj-m := hello.o


default:

        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules


clean:

        rm *.ko *.o Module.* modules.* *.mod.c

KERNELDIR 에는 커널헤더 경로가 들어가기 때문에 위와 같은 명령을 사용하면 헤더의 경로를 가져올 수 있다.
자 이제 make 를 실행해 보자.

root@cranix-desktop:~/work/drivers# make

make -C /lib/modules/2.6.31-20-generic-pae/build M=/root/work/drivers modules

make[1]: Entering directory `/usr/src/linux-headers-2.6.31-20-generic-pae'

  CC [M]  /root/work/drivers/hello.o

  Building modules, stage 2.

  MODPOST 1 modules

  CC      /root/work/drivers/hello.mod.o

  LD [M]  /root/work/drivers/hello.ko

make[1]: Leaving directory `/usr/src/linux-headers-2.6.31-20-generic-pae'

위와 같이 나오면 제대로 컴파일 된 것이고 hello.ko 파일이 생겼을 것이다.
ko 란 커널 오브젝트의 약자로서 2.6 넘어오면서 커널오브젝트와 다른 오브젝트를 구분하기위해 생긴것이다.


hello 디바이스 적재및 삭제

root@cranix-desktop:~/work/drivers# insmod hello.ko

root@cranix-desktop:~/work/drivers# lsmod |grep hello

hello                   1052  0

root@cranix-desktop:~/work/drivers# rmmod hello

root@cranix-desktop:~/work/drivers# lsmod |grep hello

root@cranix-desktop:~/work/drivers#  

위와같이 디바이스 드라이버는 insmod 와 rmmod 로 적재와 삭제를 하고 lsmod 로 리스트를 볼 수 있다.



커널로그 보기
분명 적재는 했는데 printk 로 출력한 메시지는 어떻게 볼 수 있을까?

root@cranix-desktop:~/work/drivers# dmesg

...

...

[ 2747.649737] Hello, world

[ 2759.753311] Goodbye, world

메시지가 쭈욱 나오는데 제일 아래보면 원하던 메시지를 볼 수 있을 것이다.



들어가며

안드로이드에서 frame buffer 를 테스트 하기위해서는 실제 타겟보드가 필요하다. 그러나 타겟보드는 각각 해당 디바이스 드라이버를 작성하는 방법이 다를 뿐더러 구하기도 어렵다. 그래서 여기서는 가장 접근하기 쉬운 goldfish 에뮬레이터의 frame buffer 를 분석해 보도록 하겠다.

 


frame buffer ?

리눅스에서 한 화면을 표현하는 메모리 라고 수 있다.

 


안드로이드에서 frame buffer

안드로이드에서 frame buffer 디바이스는 "/dev/graphics/fb*" 같은 형태로 표시된다.

대부분 "fb0" 이 화면에 출력하는 디바이스가 된다.

만약 출력포트가 더 존재한다면 fb1, fb2, fbn ... 이런식으로 늘어나게 된다.

이러한 video 관련 디바이스 드라이버는 안드로이드 커널의 "drivers/video" 디렉토리에 위치한다.

 


goldfish frame buffer device driver

커널 디바이스 소스코드는 "drivers/video/goldfishfb.c" 파일이다.

...

...

static struct platform_driver goldfish_fb_driver = {

.probe = goldfish_fb_probe,

.remove = goldfish_fb_remove,

.driver = {

.name = "goldfish_fb"

}

};


static int __init goldfish_fb_init(void)

{

return platform_driver_register(&goldfish_fb_driver);

}


 void __exit goldfish_fb_exit(void)

{

platform_driver_unregister(&goldfish_fb_driver);

}


module_init(goldfish_fb_init);

module_exit(goldfish_fb_exit);

위와같이 디바이스 module_init 과 module_exit 함수에 의해 커널에 등록되고 삭제된다.

이를 거치게 되면 "/dev/graphics/fb0" 이 생기게 되고 유저스페이스의 어플리케이션에서 접근 가능하게 된다.



안드로이드 에서의 frame buffer device driver 사용

안드로이드에서 frame buffer device driver 를 사용하는 부분을 찾으려면 "/dev/graphics" 을 가지고 있는 파일을 검색하면 된다.

# grep '/dev/graphics' * -R

...

hardware/libhardware/modules/gralloc/framebuffer.cpp:            "/dev/graphics/fb%u"

system/core/init/devices.c:    { "/dev/graphics/",     0660,   AID_ROOT,       AID_GRAPHICS,   1 },

system/core/init/devices.c:            base = "/dev/graphics/";

system/core/init/logo.c:    fb->fd = open("/dev/graphics/fb0", O_RDWR);

system/core/toolbox/rotatefb.c:    char *fbdev = "/dev/graphics/fb0";

system/core/adb/framebuffer_service.c:    fb = open("/dev/graphics/fb0", O_RDONLY);

...

가장 덜 의심가는 "adb/framebuffer_service.c" 파일부터 찾아보자.

이름으로 검색해 보았더니 걸리는 부분은 "adb" 밖에 없다.

adb 는 안드로이드 디버그 브릿지의 약자로서 디버그 관련된 것 일테니 일단 용의선상에서 제외한다.


rotatefb.c 역시 검색해 봤지만 별다른게 없다.

그냥 혼자 실행되는 파일인것 같다.

그래서 용의선상에서 제외시킨다.


init 에 있는 devices.c 파일은 디바이스 노드를 init 시 생성해 주고 uevent 로 오는 핫플러그 디바이스들의 생성도 담당한다.

파일은 생성만 할 뿐 관여는 하지 않는다.


그럼 logo.c 파일은 이름만 딱 보아도 로고를 출력해주는 파일일 것이다. 거기에다가 init 에 들어있으니 부팅때 나오는 로고를 출력하는 파일로 예상된다.

역시나 logo.c 파일은 부팅시 이미지 파일을 frame buffer 로 바로 뿌려주는 역할을 한다.


마지막으로 남은것은 "hardware/libhardware/modules/gralloc/framebuffer.cpp" 이다.

framebuffer.cpp 에서는 실제로 디바이스를 오픈하는 mapFrameBufferLocked() 함수를 제공한다.

 int mapFrameBufferLocked(struct private_module_t* module)

{

    // already initialized...

    if (module->framebuffer) {

        return 0;

    }


    char const * const device_template[] = {

            "/dev/graphics/fb%u",

            "/dev/fb%u",

            0 };


    int fd = -1;

    int i=0;

    char name[64];


    while ((fd==-1) && device_template[i]) {

        snprintf(name, 64, device_template[i], 0);

        fd = open(name, O_RDWR, 0);

        i++;

    }

    if (fd < 0)

        return -errno;

    ...

    ...

}

mapFrameBufferLocked() 함수는 gralloc.cpp 파일에서 사용하게 된다.

또 gralloc 어딘가에서 사용 될꺼다.

이부분에서 더 따라 올라가고 싶었지만 gralloc 함수를 제대로 끌어다 쓰는곳을 발견할 수 없었다.


그래서 조금 방식을 바꾸어서 약간 위에서 내려왔다.

일단 framebuffer 안드로이드의 surface 쪽에서 쓰인다는 것을 알고 있으니까 surfaceflinger 쪽을 보기로 했다.

frameworks/base/libs/surfaceflinger/SurfaceFlinger.cpp 파일을 보자.

 status_t SurfaceFlinger::readyToRun()

{

    LOGI(   "SurfaceFlinger's main thread ready to run. "

            "Initializing graphics H/W...");


    // we only support one display currently

    int dpy = 0;


    {    

        // initialize the main display

        GraphicPlane& plane(graphicPlane(dpy));

        DisplayHardware* const hw = new DisplayHardware(this, dpy);

        plane.setDisplayHardware(hw);

    }    

   ...

사실 이 파일은 바인더와 관련있기 때문에 상당히 복잡하고 길다.

그러나 readyToRun() 함수를 보게되면 무언가 우리가 원하는게 있는 것을 볼 수 있다.

즉 어디선가 이 함수가 호출되면 "graphincs 하드웨어가 초기화 됩니다" 라는 로그를 뿌리면서 DisplayHardware 를 생성한다.

결국 하드웨어 관련부분은 DIsplayHardware 클래스에서 처리하는걸로 판단된다.

frameworks/base/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp 파일을 열어보자

void DisplayHardware::init(uint32_t dpy)

{

    mNativeWindow = new FramebufferNativeWindow();

    framebuffer_device_t const * fbDev = mNativeWindow.getDevice();

    ...
음.. 여기서 또 FramebufferNativeWindow 를 생성한다.

클래스 명에서 수상한 냄새가 물씬 풍긴다.

frameworks/base/libs/ui/FramebufferNativeWindow.cpp 파일을 연다.

 FramebufferNativeWindow::FramebufferNativeWindow()

    : BASE(), fbDev(0), grDev(0), mUpdateOnDemand(false)

{

    hw_module_t const* module;

    if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module) == 0) {

        int stride;

        int err;

        err = framebuffer_open(module, &fbDev);

        LOGE_IF(err, "couldn't open framebuffer HAL (%s)", strerror(-err));

    

        err = gralloc_open(module, &grDev);

        LOGE_IF(err, "couldn't open gralloc HAL (%s)", strerror(-err));


        // bail out if we can't initialize the modules

        if (!fbDev || !grDev)

            return;

    ...
    ...

framebuffer_open gralloc_open 을 찾아보면

"hardware/libhardware/include/hardware/gralloc.h" 파일에 있는것을 알 수있다.

아 결국 다시 gralloc.cpp 와 가까워 진 것을 알 수 있다.

그러나 이것만 가지고 구체적으로 어떻게 접근하는지는 확인 할 수 없다.

더 나가보자.

먼저 gralloc.h 파일을 열어보자.

typedef struct alloc_device_t {

    struct hw_device_t common;

    ...

}


typedef struct framebuffer_device_t {

    struct hw_device_t common;

    ...

}

 static inline int gralloc_open(const struct hw_module_t* module,

        struct alloc_device_t** device) {

    return module->methods->open(module,

            GRALLOC_HARDWARE_GPU0, (struct hw_device_t**)device);

}


static inline int gralloc_close(struct alloc_device_t* device) {

    return device->common.close(&device->common);

}



static inline int framebuffer_open(const struct hw_module_t* module,

        struct framebuffer_device_t** device) {

    return module->methods->open(module,

            GRALLOC_HARDWARE_FB0, (struct hw_device_t**)device);

}


static inline int framebuffer_close(struct framebuffer_device_t* device) {

    return device->common.close(&device->common);

}

생각에 module->methods->open 함수에서 생성이 이루어 질것같지만 검색해서 나오지는 않는다.

결국 해당 구조체를 생성하는 부분을 찾아보자.

이전 FramebufferNativeWindow.cpp 에서 hw_get_module() 에 의해서 hw_module_t 가 초기화 되는것을 알 수 있는데 이부분을 찾으면 뭔가 나올꺼 같다.


검색해보면 hw_device_t, hw_module_t 구조체는 "hardware/libhardware/include/hardware/hardware.h" 에서 정의하고 있는것을 알 수 있다. 그럼 이 구조체를 초기화 하는 함수인 hw_get_module() 소스를 보기위해 "hardware/libhardware/hardware.c" 파일을 열어보자.

 int hw_get_module(const char *id, const struct hw_module_t **module)

{

    int status;

    int i;

    const struct hw_module_t *hmi = NULL;

    char prop[PATH_MAX];

    char path[PATH_MAX];


    /* Loop through the configuration variants looking for a module */

    for (i=0 ; i<HAL_VARIANT_KEYS_COUNT+1 ; i++) {

        if (i < HAL_VARIANT_KEYS_COUNT) {

            if (property_get(variant_keys[i], prop, NULL) == 0) {

                continue;

            }

            snprintf(path, sizeof(path), "%s/%s.%s.so",

                    HAL_LIBRARY_PATH, id, prop);

        } else {

            snprintf(path, sizeof(path), "%s/%s.default.so",

                    HAL_LIBRARY_PATH, id);

        }

        if (access(path, R_OK)) {

            continue;

        }

        /* we found a library matching this id/variant */

        break;

    }


    status = -ENOENT;

    if (i < HAL_VARIANT_KEYS_COUNT+1) {

        /* load the module, if this fails, we're doomed, and we should not try

         * to load a different variant. */

        status = load(id, path, module);

    }

    return status;

}


static int load(const char *id,   const char *path,   const struct hw_module_t **pHmi)

{

    int status;

    void *handle;

    struct hw_module_t *hmi;


    /*

     * load the symbols resolving undefined symbols before

     * dlopen returns. Since RTLD_GLOBAL is not or'd in with

     * RTLD_NOW the external symbols will not be global

     */

    handle = dlopen(path, RTLD_NOW);

    if (handle == NULL) {

        char const *err_str = dlerror();

        LOGE("load: module=%s\n%s", path, err_str?err_str:"unknown");

        status = -EINVAL;

        goto done;

    }


    /* Get the address of the struct hal_module_info. */

    const char *sym = HAL_MODULE_INFO_SYM_AS_STR;

    hmi = (struct hw_module_t *)dlsym(handle, sym);

    if (hmi == NULL) {

        LOGE("load: couldn't find symbol %s", sym);

        status = -EINVAL;

        goto done;

    }


    /* Check that the id matches */

    if (strcmp(id, hmi->id) != 0) {

        LOGE("load: id=%s != hmi->id=%s", id, hmi->id);

        status = -EINVAL;
        goto done;
    }

    hmi->dso = handle;

    /* success */
    status = 0;

    done:
    if (status != 0) {
        hmi = NULL;
        if (handle != NULL) {
            dlclose(handle);
            handle = NULL;
        }
    } else {
        LOGV("loaded HAL id=%s path=%s hmi=%p handle=%p",
                id, path, *pHmi, handle);
    }

    *pHmi = hmi;

    return status;
}

먼저 윗쪽에 sprintf 함수에 의해 path 문자열이 생성된다. 이것은 결국 "/system/lib/hw/gralloc.default.so" 로 생성된다. 이걸 인자로 load 로 넘겨버리는데 load 로 가보자.


여기에 dlopen,dlclose 그리고 dlsym 부분이 있다.

처음 보는 함수인데 아무리 봐도 시스템콜 이다.

구글링 해봤더니 아래에 잘 정리된 자료가 있다.

 http://wiki.kldp.org/HOWTO/html/Program-Library-HOWTO/dl-libraries.html

이것들의 역할은 동적 라이브러리를 로딩해서 dlsym 으로 라이브러리의 심볼값을 찾아서 주는것이다.

여기서 심볼값을 찾아 준다는것은 함수나 전역변수의 주소를 돌려준다는 얘기다.


아.. 결국은  HAL 쪽을 so 형태로 만들어서 올린다음에 필요할때만 동적으로 로딩해서 쓰겠다는 의미이다.

내생각에 이부분은 구글에서 HAL 쪽 표준 인터페이스를 정의해 놓은것으로 판단된다.

이부분은 나중에 정확하게 정리해야 할것 같다.


이제 dlsym 함수 호출하는 부분에서 정의한 심볼값을 추적해 보면 계속 따라갈 수 있을꺼 같다.

위 함수에서 dlsym 으로 가져오는 심벌은 HAL_MODULE_INFO_SYM_AS_STR 이다 이것은 "hardware/libhardware/include/hardware/hardware.h" 파일에 정의되어 있고 값은 "HMI" 이다.


dlsym 으로 가져온 내용을 hw_module_t 형으로 형변환 했다는것을 미루어 볼때 gralloc.default.so 파일에 HMI 라는 이름으로 hw_module_t 형의 전역변수가 선언되어 있다고 판단된다.

그럼 "/hardware/libhardware/modules/gralloc/gralloc.cpp" 파일을 열어서 확인해 보자.

 static struct hw_module_methods_t gralloc_module_methods = {

        open: gralloc_device_open

};

struct private_module_t HAL_MODULE_INFO_SYM = {

    base: {

        common: {

            tag: HARDWARE_MODULE_TAG,

            version_major: 1,

            version_minor: 0,

            id: GRALLOC_HARDWARE_MODULE_ID,

            name: "Graphics Memory Allocator Module",

            author: "The Android Open Source Project",

            methods: &gralloc_module_methods

        },

        registerBuffer: gralloc_register_buffer,

        unregisterBuffer: gralloc_unregister_buffer,

        lock: gralloc_lock,

        unlock: gralloc_unlock,

    },  

    framebuffer: 0,

    flags: 0,

    numBuffers: 0,

    bufferMask: 0,

    lock: PTHREAD_MUTEX_INITIALIZER,

    currentBuffer: 0,

    pmem_master: -1,

    pmem_master_base: 0

};

HAL_MODULE_INFO_SYM 이부분은 쌍따움표를 뺀 HMI 이다.

결국 같은 이름의 전역변수가 있다. 그런데 리턴형이 private_module_t 형이다.

아마도 private_module_t 구조체는 가장 첫 필드에 hw_module_t 형을 가지고 있는듯 하다.


"/hardware/libhardware/modules/gralloc/gralloc_priv.h" 파일에 해당 구조체가 선언되어 있는데 아래와 같다.

 struct private_module_t {

    gralloc_module_t base;


    private_handle_t* framebuffer;

    uint32_t flags;

    uint32_t numBuffers;

    uint32_t bufferMask;

    pthread_mutex_t lock;

    buffer_handle_t currentBuffer;

    int pmem_master;

    void* pmem_master_base;


    struct fb_var_screeninfo info;

    struct fb_fix_screeninfo finfo;

    float xdpi;

    float ydpi;

    float fps;

    

    enum {

        // flag to indicate we'll post this buffer

        PRIV_USAGE_LOCKED_FOR_POST = 0x80000000

    };  

};


가장 첫 필드가 기대했던 hw_module_t 형이 아니다. 이것은 gralloc_module_t 형의 첫 필드에 hw_module_t 형을 가지고 있을수 있다는 말이니 또 찾아가 보자.

"hardware/libhardware/include/hardware/gralloc.h" 파일에 보면 아래와 같은 부분이 있다.

typedef struct gralloc_module_t {

    struct hw_module_t common;

    

    int (*registerBuffer)(struct gralloc_module_t const* module,

            buffer_handle_t handle);


    int (*unregisterBuffer)(struct gralloc_module_t const* module,

            buffer_handle_t handle);

    ...
} galloc_module_t
이제야 찾았다.

이것은 마치 구조체로 상속을 구현한 것과 같은 효과를 가진다.


이제 gralloc.h 에 있던 module->methods->open() 함수를 호출하게되면 gralloc_device_open() 함수가 실행 된다는것을 알 수 있다.

"hardware/libhardware/modules/gralloc/gralloc.cpp" 파일을 열어보자.

 int gralloc_device_open(const hw_module_t* module, const char* name,

        hw_device_t** device)

{

    int status = -EINVAL;

    if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) {

        gralloc_context_t *dev;

        dev = (gralloc_context_t*)malloc(sizeof(*dev));


        /* initialize our state here */

        memset(dev, 0, sizeof(*dev));


        /* initialize the procs */

        dev->device.common.tag = HARDWARE_DEVICE_TAG;

        dev->device.common.version = 0;

        dev->device.common.module = const_cast<hw_module_t*>(module);

        dev->device.common.close = gralloc_close;


        dev->device.alloc   = gralloc_alloc;

        dev->device.free    = gralloc_free;


        *device = &dev->device.common;

        status = 0;

    } else {

        status = fb_device_open(module, name, device);

    }

    return status;

}

fb_device_open() 함수를 호출한다. 결국 우리가 처음에 찾았던 framebuffer.cpp 파일로 돌아왔다.

"hardware/libhardware/modules/gralloc/framebuffer.cpp" 파일을 열어보자

 int fb_device_open(hw_module_t const* module, const char* name,

        hw_device_t** device)

{

    int status = -EINVAL;

    if (!strcmp(name, GRALLOC_HARDWARE_FB0)) {

        alloc_device_t* gralloc_device;

        status = gralloc_open(module, &gralloc_device);

        if (status < 0)

            return status;


        /* initialize our state here */

        fb_context_t *dev = (fb_context_t*)malloc(sizeof(*dev));

        memset(dev, 0, sizeof(*dev));


        /* initialize the procs */

        dev->device.common.tag = HARDWARE_DEVICE_TAG;

        dev->device.common.version = 0;

        dev->device.common.module = const_cast<hw_module_t*>(module);

        dev->device.common.close = fb_close;

        dev->device.setSwapInterval = fb_setSwapInterval;

        dev->device.post            = fb_post;

        dev->device.setUpdateRect = 0;


        private_module_t* m = (private_module_t*)module;

        status = mapFrameBuffer (m);

        if (status >= 0) {

            int stride = m->finfo.line_length / (m->info.bits_per_pixel >> 3);             const_cast<uint32_t&>(dev->device.flags) = 0;

            const_cast<uint32_t&>(dev->device.width) = m->info.xres;

            const_cast<uint32_t&>(dev->device.height) = m->info.yres;

            const_cast<int&>(dev->device.stride) = stride;

            const_cast<int&>(dev->device.format) = HAL_PIXEL_FORMAT_RGB_565;

            const_cast<float&>(dev->device.xdpi) = m->xdpi;

            const_cast<float&>(dev->device.ydpi) = m->ydpi;

            const_cast<float&>(dev->device.fps) = m->fps;

            const_cast<int&>(dev->device.minSwapInterval) = 1;

            const_cast<int&>(dev->device.maxSwapInterval) = 1;

            *device = &dev->device.common;

        }

    }

    return status;

}

static int mapFrameBuffer(struct private_module_t* module)

{

    pthread_mutex_lock(&module->lock);

    int err = mapFrameBufferLocked(module);

    pthread_mutex_unlock(&module->lock);

    return err;

}

mapFrameBuffer 를 호출하고 그 함수 안에서 mapFrameBufferLocked 호출하여 마침내 framebuffer 디바이스를 생성하게 된다.



마치며

결국 디바이스 드라이버에서 surfaceflinger 까지 이런식으로 연결되어 있는 것이다.

다음번에는 HAL 인터페이스 부분을 분석해 봐야 겠다.

curl 로 repo 스크립트 받기

안드로이드는 수많은 개발자가 참여하는 오픈소스 시스템이다.

이러한 시스템을 관리해주는 유틸은 git 를 사용하는데 그러다 보니 전체 소스가 한꺼번에 관리되지 않는다.

그래서 웹상에 소스를 한꺼번에 다운로드 받을수 있는 스크립트를 작성해 놓고 curl 로 다운받아가게 한 것이다.

# apt-get install curl

# curl http://android.git.kernel.org/ repo

# chmod 755 repo

이제 repo 라는 스크립트가 생겼을 것이다.

편하게 하기위해서는 repo 가 있는 디렉토리를 PATH 로 잡아놓으면 된다.

 

 

repo 를 사용해서 소스받기

repo 는 결국 git 를 사용하기 때문에 git 를 먼저 설치해야 한다.

 # apt-get install git-core

git-core 가 없다면 repo 를 실행했을때 "No such file or directory" 오류가 떨어질 것이다.

 

repo 를 이용해서 다운받으려면 init 과 sync 옵션을 사용하면 된다.

# mkdir android_src

# cd android_src

# repo init -u git://android.git.kernel.org/platform/manifest.git

# repo sync

repo 를 실행하기 전에 먼저 소스가 저장될 디렉토리를 만들고 그안에서 init 과 sync 작업을 하도록 하자.

repo init 옵션은 마지막에 -b (branch) 옵션으로 원하는 버전의 소스를 받을수 있다. 위 처럼 아무것도 안쓴다면 최신버전을 다운받게 된다.

그리고 init 을 실행 때 이름, 메일주소 등을 물어보는데 적당히 적어주면 된다.

init 작업은 실제로 소스를 다운받지 않기 때문에 빠른 시간안에 끝난다. sync 에서 비로소 소스를 다운받기 때문에 상당히 시간이 걸린다. 인터넷이 빨라도 최소한 한시간은 잡아야 한다.

 

태그 : Android,CURL,repo

우분투 셋팅

분류없음 2010/03/18 00:19
들어가며
어제 새벽까지 설치하고 일하러 갔더니 완전 제정신이 아니었다.
오늘은 얼른하고 자야겠다.
일단 오늘 할것을 생각해 보자.

  폰트, vim, eclipse, ...


폰트셋팅
처음 우분투 설치할때는 폰트가 맘에든다고 생각했지만,
계속 쓰다보니 별로 맘에 안들었다.
일단 터미널 폰트부터 바꿔보자.
폰트는 한컴에서 만든 네이버사전체와 고정폭 Lucida Sans Typewriter 폰트를 받도록 하자.

 함초롬체 : http://www.haansoft.com/hnc/event/ham/index.htm

 네이버사전체 : http://cndic.naver.com/static/fontInstall

 고정폭 Lucida Sans Typewriter 체 : # apt-get install sun-java6-fonts

다 받았으면 아래와 같이 폰트 디렉토리의 truetype 디렉토리에 회사별로 디렉토리를 만들고 ttf 파일을 이동시키자.
일단 아래와같이 폰트에 디렉토리를 만들고 폰트캐시를 업데이트 하자.

 # mkdir /usr/share/fonts/truetype/han

 # mv *.ttf /usr/share/fonts/truetype/han

 # fc-cache -v

그럼 이제 폰트를 바꿔보자.
시스템전체의 글꼴을 설정하려면 아래 메뉴로 가서 변경하자.

 시스템 -> 기본설정 -> 모양새 -> 폰트


VI 셋팅
일단 VI 는 먼저 vim 을 설치하도록 하자.
그다음 홈 디렉토리 아래에 .vimrc 파일을 만들고 아래와 같이 저장하자.

set autoindent          "자동 들여쓰기

set cindent             "C 프로그래밍 할때 자동으로 들여쓰기

set smartindent         "좀 더 똑똑한 들여쓰기

set nobackup            "백업파일을 만들지 않는다.

set number              "라인번호를 출력한다.

"폰트 설정

set fenc=utf-8

set fencs=utf-8,cp949,cp932,euc-jp,shift-jis,big5,latin1,ucs-2le

set nocp                " vi 와 호환성을 없애고 vim 만 쓸수있게


filetype on  "파일 종류를 자동으로 인식

set ru "커서의 위치를 항상 보이게함

set sc "완성중인 명령을 표시


set background=dark

colorscheme elflord

filet plugin indent on "파일종류 자동으로 인식

syntax on "알아서 하이라이팅


if has("gui_running") "gui 시작이면 시작시 크기 설정

   set lines=50

   set co=125

endif

참고로  /etc/skel 디렉토리는 사용자가 생성될 때마다 복사하기 때문에 거기에 .vimrc 를 옮겨놓으면 나중에 편해진다.


오픈오피스 글자 깨지는거 수정
오픈오피스를 켰더니 글자가 깨져서 얼마전에 내가 적었던 포스트를 검색해서 수정했다.
이 블로그에서 "오픈오피스" 로 검색하면 나올것이다.


마치며
얼른 하려고 했는데 또 한시가 넘어버렸다.ㅜ
내일은 이클립스 설정이나 해야겠다.

태그 : font,ubuntu,vi