'개발'에 해당되는 글 5건

  1. 2012.08.28 [C언어] 좀비 프로세스를 없애는 더블 fork().. (3)
  2. 2012.08.28 [C언어] 쓰레드의 사용 (1)
  3. 2012.07.11 [ExtJs] 컨포넌트의 itemId와 id의 차이점 (1)
  4. 2012.06.29 Extjs4에서 Custom event 만들기 (1)
  5. 2012.06.28 [ExtJS 4] initComonent함수에서 Component Query 사용시 유의점 (1)
2012. 8. 28. 18:51

[C언어] 좀비 프로세스를 없애는 더블 fork()..

fork()를 이용하여 서버 프로그래밍을 할 시, 유용한 방법을 소개하기로 한다. 이는 Steven아저씨 책 Advanced Programming in the Unix Environment 202쪽에 나와 있는 방법이기도 하다.


우선 좀비 프로세스에 대해서 알아야 한다.

좀비 프로세스:

프로세스가 뒤졌는데, 아무도 이놈의 장례를 치뤄주지 않아 뒤진 상태로 리소스를 몽땅 가지고 있는 상태..


뭐 이미 알겠지만, 프로세스가 뒤지면 가지고 있던 파일들 할당 받았던 모든 메모리를 자동으로 모두 해제되고 참 좋으다. 하지만, 장례를 치뤄주지 않으면, 그냥 좀비가 되어서 내 서버를 잠식하고, 결국에는 서버 전체적으로 fork()가 안되는 매우 위험한 상황까지 발생한다.


그럼 좀비 프로세스는 누가 장례를 치뤄주는가..?

인간 세상과는 다르게 부모가 치뤄준다. 이를 위한 함수가 waitpid(pid_t childpid)이다. waitpid()를 호출하여 해당 childpid의 자식 프로세스가 죽으면 부모 프로세스가 이 함수를 통해서 자식의 main함수의 리턴값을 결과로 받게 되며, 그와 동시에 자식이 가지고 있던 모든 리소스가 해제된다.


그렇다면 몇가지 의문점이 생긴다.

1. 부모가 먼저 죽은 경우는 어찌되느가?

2. 자식이 waitpid()호출전에 죽으면 어찌 되는가?


첫째, 부모가 먼저 죽으면 자식은 고아가 되고, 이 고아 프로세스들을 init 프로세스가 엄마노릇을 대신 하게 된다. 즉, 자식이 살아 있는데 부모가 죽으면, 자식의 부모가 init프로세스로 자동 변경된다.

둘째, 자식이 먼저 죽었는데 부모가 waitpid()로 이를 해제해주지 않으면 일단 좀비 프로세스가 되고, 그후, waitpid()를 호출하는 순간 바로 리턴되면서 자식의 리소스가 해제된다.


#include 
#include 
#include 

int main(int argc, char **argv)
{
    pid_t pid;

    if( (pid = fork()) > 0 )
    {
        while(1);
    }
    else if( pid == 0 )
    {
        return 0;
    }
    else
    {
        int errsv = errno;
        perror("fork error");
        return errsv;
    }

    return 0;
}


위 소스는 fork를 하고, 부모는 무한루프에 빠진 후에, 자식은 바로 죽게 하였다. 그런 후, ps로 상태를 확인하면 다음과 같이 좀비가 되어 있는 자식을 볼 수 있다.



zombie는 프로세스 이름이고, 중간에 보면 Z+ 라고 표시되어 있는데 바로 현재 좀비상태가 되었다는 것을 의미한다. 


그렇다면 좀비를 없애기 위해서 어찌해야 할 것인가? 단순히 waitpid()를 호출하면 장땡인가? 맞다!! 하지만, 문제는 서버도 자식이 죽기를 기다리고만 있는 것을 원하지 않는다는 것이다. 괜히 불렀다가 block되어 버리면 서버는 놀아야 하니까.. 그렇다고 주기적으로 nonblock형태로 불러주는 것도 비효율적이다.


그래서 다음과 같은 시나리오로 zombie 프로세스가 생성되지 않도록 한다!!

  1. A 프로세스는 fork하여 자식 B를 만든다.
  2. 자식 B는 다시 fork하여 자식 C를 만든다.
  3. 자식 B는 바로 죽어 버린다.(ASAP!!) => C는 부모(B)가 죽었으므로 부모가 init로 변경된다. (A와는 전혀 상관 없음)
  4. A가 자식B를 waitpid()로 장례를 치뤄버린다. (정상적으로 종료)

정말 아름다운 기법이 아닐 수 없다!!!

자 예제로 이를 실현해 보도록 하자.


#include 
#include 
#include 

int main(int argc, char **argv)
{
    pid_t pid;

    if( (pid = fork()) > 0 )
    {
        waitpid(pid, NULL, 0);
        while(1);
    }
    else if( pid == 0 )
    {
        if( (pid = fork()) > 0 )
        {
            return 0;
        }
        else if(pid == 0)
        {
            sleep(2);
        }

        return 0;
    }
    else
    {
        int errsv = errno;
        perror("fork error");
        return errsv;
    }

    return 0;
}



이와 같이 했을 경우 ps로 결과를 보면 다음과 같다.


직후



2초후, 손자 프로세스 사망 후


자 깔끔하게 자식 프로세스가 종료되었으며, zombie는 발생하지 않는다..!!





'개발 > Server' 카테고리의 다른 글

[C언어] 좀비 프로세스를 없애는 더블 fork()..  (3) 2012.08.28
[C언어] 쓰레드의 사용  (1) 2012.08.28
Trackback 0 Comment 3
  1. Favicon of http://ad.planchasghden.com/ BlogIcon plancha ghd 2013.04.12 14:51 address edit & del reply

    파울로 로베르시와 샤넬의 디자이너 칼 라거펠트 등 세계 최고의 컨텐츠 메이커들과의 작업에 대한 에피소드와 결과물까지 함께 만나볼 수 있다.

  2. Favicon of http://plk.mbablueprint.com/ BlogIcon Michael Kors purses 2013.04.24 21:06 address edit & del reply

    정직을 잃은 자는 더 이상 잃을 것이없다.

  3. Favicon of http://7556.morningcallcoffeesstand.com/ChicagoBlackhawks-us.php BlogIcon Chicago Blackhawks Jersey 2013.07.19 18:20 address edit & del reply

    당신 매력있어, 자기가 얼마나 매력있는지 모르는게 당신매력이야

2012. 8. 28. 14:41

[C언어] 쓰레드의 사용

쓰레드는 사용하기 가장 까다로운 녀석이다.

대부분의 어설픈 개발자들이 무턱대고 쓰다가 프로젝트가 끝나지 못하는 경험을 하게 되며, 그렇지 않더라도 자신의 잘못을 모르고 어설프게 쓰래드를 쓰고 있을 것 같다.


여기서 이야기 하고자 하는 것은 매우 간단한 몇가지만 짚고 넘어간다. 실수하기 쉬운 내용들...


쓰레드의 시작

#include <pthread.h>

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

쓰레드는 이 함수로부터 시작된다. 그리고 쓰레드가 죽는 원인은 다음 3가지로 정의할 수 있다.

  1. 쓰레드 자신이 pthread_exit를 호출한 경우: 해당 함수 인자를 종료 값으로 pthread_join()을 통해 넘겨준다.
  2. start_routine에서 리턴한 경우: main()함수가 리턴한 것과 같은 효과인데 이 경우는 쓰레드가 종료되며, 1번과 동일하게 pthread_join()에 의해서 결과값을 전달받을 수 있다.
  3. 취소가 된 경우: pthread_cancel
하지만 머니머니 해도 메인쓰래드가 죽으면 다 죽는다.



여기에서 신경써야 하는 부분은 start_routine와 arg이다. 다음 코드는 정상작동하지 않는다.


static int threadStart(int socket)
{
    pthread_t thread_id;
	
    if( pthread_create(&thread_id, NULL, threadMain, (void*) &socket) != 0 )
    {
        int errsv = errno;
        ERROR_SYSTEM();
		ERROR_ASSERT(FALSE);
		return errsv;
    }
    
    return E_OK;
}

static void *threadMain(void *_socket)
{
    int socket = (int) *((int *)_socket);


이 경우 정상작동하지 않는 이유는 다음과 같다.

pthread_create()를 호출할 때 전달한 socket이 문제가 된다. socket은 함수의 인자이므로 자동변수(로컬변수)이며, 함수가 종료되면 자동 해제된다.

그런데, threadMain()함수의 시작 시점은 공식적으로 전혀 알수가 없다. 다시말해, threadStart함수가 종료된 후 호출될 수 있다. 그런 경우, 다른 프로세스 과정 또는 다른 쓰레드에 의해서 해당 영역(스텍영역)이 침범당해서 전혀 다른 값으로 바뀌게 된다.

이를 방지하기 위한 해결책은 쓰레드에 전달하는 인자를 malloc으로 할당하여 heap영역에 저장한 후, theadMain에서 이를 해제 하는 방식이다.


typedef struct {
	int socket;
}socket_pack_t;

static int threadStart(int socket)
{
    pthread_t thread_id;
	socket_pack_t *thread_param = (socket_pack_t *) malloc(sizeof(socket_pack_t));
	memset(thread_param, '\0', sizeof(socket_pack_t));
	thread_param->socket = socket;

    if( pthread_create(&thread_id, NULL, threadMain, thread_param) != 0 )
    {
        int errsv = errno;
        ERROR_SYSTEM();
		ERROR_ASSERT(FALSE);
		return errsv;
    }
    
    return E_OK;
}

static void *threadMain(void *_socket)
{
	socket_pack_t *param = (socket_pack_t *)_socket;
    int socket =  param->socket;
	
	free(param);	//	해제 한다.



이와 같이 하면, 쓰레드의 파라메터가 heap영역에 할당된 메모리에 저장되며, thread에 전달된 후에, 해제될 수 있다.

'개발 > Server' 카테고리의 다른 글

[C언어] 좀비 프로세스를 없애는 더블 fork()..  (3) 2012.08.28
[C언어] 쓰레드의 사용  (1) 2012.08.28
Trackback 0 Comment 1
  1. Favicon of http://7261.cicnewsst.com BlogIcon toms outlet 2013.07.20 19:23 address edit & del reply

    태양이 바다에 미광을 비추면,나는 너를 생각한다.

2012. 7. 11. 17:40

[ExtJs] 컨포넌트의 itemId와 id의 차이점

ExtJS나 Sencha touch를 사용하여 프로그래밍을 하면 비번하게 컨포넌트를 쿼리해서 새팅하거나 값을 얻어오는 경우가 있다. 이때, 많이 사용하는 방법이 컨포넌트 쿼리에 xtype(alias)를 넣어 찾는 방법이다.


	update: function (data) {
		var topicField = this.down('textfield'),
		     objectiveField = this.down('textarea');
               // Do Something w/ topicField & objeictive
       }


다행히도 위와 같이 레퍼런스해야 할 컨포넌트의 xtype이 다르다면 쉽지만, 동일하다면 id를 사용하여야 한다. 


Ext.define('MyClass', {
        extend: 'Ext.panel.Panel',
        alias: 'mypanel',
        initComponent: function () {
            Ext.apply(this, {
			items: [{
				xtype: 'textfield',
				id: 'topic'
				fieldLabel: '주제',
				margin: '2 2 2 2',
				allowBlank: false,
				value: todo
			}, {
				xtype: 'textfield',
				fieldLabel: '목적',
                                id: 'objecive',
				margin: '2 2 2 2',
				name: 'objective',
				value: todo
			}]
            });
            this.callParent(arugments);
        },

	update: function (data) {
		var topicField = this.down('#topic'),
		     objectiveField = this.down('#objective');
               // Do Something w/ topicField & objeictive
       }


그러나, 위와 같이 id를 컨포넌트에 지정한 경우, MyClass의 인스턴스를 한번이상 생성시, 충돌이 발생한다.

이를 방지하기 위해서는 itemId를 사용할 수 있다.


Ext.define('MyClass', {
        extend: 'Ext.panel.Panel',
        alias: 'mypanel',
        initComponent: function () {
            Ext.apply(this, {
			items: [{
				xtype: 'textfield',
				itemId: 'topic'
				fieldLabel: '주제',
				margin: '2 2 2 2',
				allowBlank: false,
				value: todo
			}, {
				xtype: 'textfield',
				fieldLabel: '목적',
                                itemId: 'objecive',
				margin: '2 2 2 2',
				name: 'objective',
				value: todo
			}]
            });
            this.callParent(arugments);
        },

	update: function (data) {
		var topicField = this.down('#topic'),
		     objectiveField = this.down('#objective');
               // Do Something w/ topicField & objeictive
       }

이와 같이 하면, MyClass의 인스턴스를 한번이상 생성하더라도 문제가 발생하지 않는다.

이는 itemId가 컨포넌트 내부의 MixedCollection의 인덱스로 존재하는 로컬 스코프를 갖고 있기 때문이다.


다음은 ExtJS의 메뉴얼 문서의 내용을 캡쳐한 것이다.

출처: http://docs.sencha.com/ext-js/4-1/#!/api/Ext.AbstractComponent-cfg-itemId

An itemId can be used as an alternative way to get a reference to a component when no object reference is available. Instead of using an id with Ext.getCmp, use itemId with Ext.container.Container.getComponent which will retrieve itemId's or id's. Since itemId's are an index to the container's internal MixedCollection, the itemId is scoped locally to the container -- avoiding potential conflicts withExt.ComponentManager which requires a unique id.

var c = new Ext.panel.Panel({ //
    height: 300,
    renderTo: document.body,
    layout: 'auto',
    items: [
        {
            itemId: 'p1',
            title: 'Panel 1',
            height: 150
        },
        {
            itemId: 'p2',
            title: 'Panel 2',
            height: 150
        }
    ]
})
p1 = c.getComponent('p1'); // not the same as Ext.getCmp()
p2 = p1.ownerCt.getComponent('p2'); // reference via a sibling

Also see idExt.container.Container.queryExt.container.Container.down and Ext.container.Container.child.



Trackback 0 Comment 1
  1. Favicon of http://hml.contact-hotel.com/cl.php BlogIcon louboutin pas cher 2013.04.12 21:16 address edit & del reply

    매우 지원, http://hml.uggpaschermz.com chaussures ugg, 아주 좋아.

2012. 6. 29. 11:27

Extjs4에서 Custom event 만들기

MVC모델을 권장하는 Extjs4에서는 Controller에서 view를 찾기 위해서 Component Query를 많이 쓰면, Controller와 View가 너무 연관되어 MVC 본연의 모습을 잃을 수 있다.

그나마 완벽한 해결책은 아니지만, Component Query를 최소화 하고, 코드를 간결하게 만들기 위해서 view를 만들 때, 자신만의 Event를 정의하여 controller에서 처리하게 하면 그나마 Controller와 View가 어느정도 독립성을 유지하는데 도움이 된다.


이를 위한 방법을 간결하게 정리하도록 한다.


다음과 같은 상황을 가정한다.

1. Panel내부에 toolbar가 있고, toolbar에 "추가"라고 되어 있는 버튼이 존재한다.

2. 이 버튼을 누르면, Panel이 'add' event를 줄 수 있도록 한다.

3. controller는 Panel 내부의 "추가"버튼까지 Component Query를 하지 않고, 단순히 Panel까지만 Component Query를 하여 'add'이벤트에 대한 핸들러를 추가하여 일을 마무리 한다.

// view file
Ext.define('MyApp.view.List', {
	extend: 'Ext.grid.Panel',
  alias: 'widget.mypanel',
	border: false,

	currentRecord: undefined,

	initComponent: function () {
		var me = this;

		this.addEvents('newwin');
		Ext.apply(this, {
			store: 'Teachings',
			columnLines: true,

			selModel: {
				selection: 'checkboxmodel'
			},
/********* 중략 *********/
			dockedItems: [{
				xtype: 'toolbar',
				items: [{
					text:'추가',
					tooltip:'새로운 교안을 작성합니다.',
					iconCls:'icon_add',
					action: 'add',
					listeners: {
						click: {
							fn: this.addEventEmitter,
							scope: this
						}
					}
				}]
/********* 후략 *********/
	addEventEmitter: function () {
this.fireEvent('add');
	}
});


이와 같이 하고 Controller에서는 이를 이용하여 이벤트 핸들러를 작성한다.


Ext.define('TeacherErp.controller.Teaching', {
	extend: 'Ext.app.Controller',

	stores: [
		'Teachings'
	],

	views: [
		'MyApp.List',
	],

	init: function () {
		this.control({
			'mypanel': {
				add: this.addHandler


요약하자면, 여기에서 custom event를 만들기 위한 순서는 다음과 같다.

1. 이벤트 이름('add')를 추가한다: this.addEvents('add');

2. 이벤트를 발생시킨다.: this.fireEvent('add');

3. 이벤트를 처리한다.


여기에서 중요한 것은 button의 listeners를 설정할 때, scope를 따로 준 것이다. 그렇지 않으면, 해당 이벤트 핸들러(addEventEmitter)의 this는 button이 되기 때문에 controller에서 MyApp.view.List를 component Query로 찾아서 이벤트를 연결해도 찾지 못한다. (단순히 이벤트 핸들러가 등록되지 않은 것이니 에러도 호출되지 않는다.)


Trackback 0 Comment 1
  1. Favicon of http://5915.stlouiscores.com BlogIcon ghd 2013.07.21 05:05 address edit & del reply

    희미한 달빛이 샘물 위에 떠있으면,나는 너를 생각한다.

2012. 6. 28. 22:20

[ExtJS 4] initComonent함수에서 Component Query 사용시 유의점

ExtJS나 Sencha Touch를 사용하다보면, Component Query를 이용하여 Component를 검색하는 경우가 매우 많습니다.


별로 해당 사항은 없지만 initComponent함수내부에서 up/down함수를 이용하여 컴포넌트를 검색하는 순간이 있는데, 검색이 되지 않을 수 있습니다. 이는 초기화의 차이인데요. 부모의 초기화 함수를 호출 한 후, 실행하면 문제없이 검색이 됩니다.


initComponent: function() {
    var me = this;
    // The method doesn't exists yet in your extended class
    me.callParent(arguments);
    // Place the code here, after you have called the parent constructor.
}



참고 링크: http://docs.sencha.com/ext-js/4-1/#!/api/Ext.container.AbstractContainer-method-down

Trackback 0 Comment 1
  1. Favicon of http://plk.lisseurghdle.com/ BlogIcon Lisseur GHD 2013.04.25 02:28 address edit & del reply

    다른 사람을 비웃지 말라, 자기의 행복이 영원한것이라고 누가 장담할 것인가.