블로그 이미지
footprintz
제대로 달려, 전력 질주 할 수 있는 시간은 의외로 짧아...

calendar

1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30

Notice

2014. 1. 20. 16:18 개발 이야기/Switch 설정

아래 사이트에 들어가서 switch 아랫면의 serial number 를  입력하면 firmware download 가 가능하다. 


http://h20565.www2.hp.com/portal/site/hpsc/template.PAGE/public/psi/swdHome?sp4ts.oid=4174722&ac.admitted=1389864208812.876444892.492883150

posted by footprintz
2014. 1. 5. 16:38 개발 이야기/Linux

이 간단한걸 x삽질, x시간 낭비.. ;;


※ 해결

1. 파일의 encoding 이 어떻게 되어있는지 확인


[root@shy ~/test ]# file -i test.c

test.c: text/x-c; charset=iso-8859-1

 ☞ iso-8859-1 로 encoding 되어있음을 확인

2. vimrc 의 fileencodings 에 iso-8859-1 추가

set fileencodings=ucs-bom,utf-8,cp949,latin1,iso-8859-1

 ☞ fileencodings : 여러 인코딩 룰을 적어주면 파일을 읽을 때 하나씩 검사해서 맞는 인코딩을 찾아내어 자동으로 fileencoding 을 세팅

posted by footprintz
2013. 12. 24. 19:41 개발 이야기/Network

HTTP does not define any limit. However most web servers do limit size of headers they accept. 


For example in Apache default limit is 8KB, in IIS it's 16K


Server will return 413 Entity Too Large error if headers size exceeds that limit.


cf)

Apache 2.0, 2.2: 8K

nginx: 4K - 8K

IIS: varies by version, 8K - 16K

Tomcat: varies by version, 8K - 48K (?!)


 - 원문 : http://stackoverflow.com/questions/686217/maximum-on-http-header-values

posted by footprintz
2013. 12. 17. 16:02 개발 이야기/Linux

하이퍼바이저 (The Hypervisor)

- VMM(Virtual Machine Monitor, or Manager)라고도 불려진다.

하이퍼바이저는 여러 개의 OS가 단일 하드웨어 호스트를 공유할 수 있도록 하는 프로그램이다.

각 OS는 호스트의 프로세서, 메모리 및 기타 자원들을 모두 스스로 나타낸다.

하이퍼바이저의 역할은 높은 수준의 관리 및 모니터링 도구에 대한 인터페이스를 제공하는 것 이외에, OS 간 서로를 방해하지 못하도록 VM에 대한 자원 및 메모리 할당 등을 처리하는 것이다.

 

 

하이퍼바이저는 크게 두가지 타입으로 나눠진다.

 

 

 

{ 참고 : Xen은 몇몇 회사들이(ORACLE, Citrix, Sun, and Virtual Iron 등등..) 가상화를 구현하기 위해 사용되는 opened-source 가상화 소프트웨어이다. }

 

 

① Type 1

 - 하이퍼바이저는(Native or Bare-Metal) 하드웨어 제어 및 Guest OS 모니터로 호스트의 하드웨어에서 직접 실행하는 소프트웨어 시스템이다.

Bare-Metal 가상화 기술은 현재 기업의 데이터센터 분야를 이끌고 있다.

VMWare ESX는 현재 기업의 가상화 시장을 이끌고 있으며, 이것은 Bare-Metal 가상화 아키텍처를 사용한다.

하이퍼바이저는 하드웨어 위에 위치해 있다. 이런 이유로 Bare-Metal 가상화라고 불려진다.

이런 이유로 많은 데이터센터들이 ESX, Xen, and Hyper-V와 같은 bare-metal 상품을 사용한다.

그 이유는, 이러한 상품들은 OS(호스트 가상화 사용)로부터 오는 오버헤드를 감소시켜 빠른 속도를 제공하기 때문이다.

 

② Type 2

 - 하이퍼바이저는(Hosted) 기존의 OS 환경에서 실행되는 소프트웨어 응용 프로그램이다.

이러한 유형의 하이퍼바이저는 일반적으로 Microsoft의 Virtual PC나 VMWare의 Workstation와 같은 클라이언트(user) 쪽에서의 가상화 프로그램이다.

 

                                                        <Type 2> // ← 전가상화의 형태이다.


※ 원문

http://blog.daum.net/_blog/BlogTypeView.do?blogid=02XHr&articleno=13719879&_bloghome_menu=recenttext

※ 참고

http://ko.wikipedia.org/wiki/%ED%95%98%EC%9D%B4%ED%8D%BC%EB%B0%94%EC%9D%B4%EC%A0%80

 

 

posted by footprintz
2013. 10. 15. 20:30 개발 이야기/Linux Kernel

Layout of SKB data area

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

This first diagram illustrates the layout of the SKB data area and where in that area the various pointers in 'struct sk_buff' point.

The rest of this page will walk through what the SKB data area looks like in a newly allocated SKB. How to modify those pointers to add headers, add user data, and pop headers.

Also, we will discuss how page non-linear data areas are implemented. We will also discuss how to work with them.



	skb = alloc_skb(len, GFP_KERNEL);

 


Layout of freshly allocated SKB This is what a new SKB looks like right after you allocate it using alloc_skb()

As you can see, the head, data, and tail pointers all point to the beginning of the data buffer. And the end pointer points to the end of it. Note that all of the data area is considered tail room.

The length of this SKB is zero, it isn't very interesting since it doesn't contain any packet data at all. Let's reserve some space for protocol headers using skb_reserve()



	skb_reserve(skb, header_len);

 


Layout of SKB after skb_reserve() This is what a new SKB looks like right after the skb_reserve() call.

Typically, when building output packets, we reserve enough bytes for the maximum amount of header space we think we'll need. Most IPV4 protocols can do this by using the socket value sk->sk_prot->max_header.

When setting up receive packets that an ethernet device will DMA into, we typically call skb_reserve(skb, NET_IP_ALIGN). By default NET_IP_ALIGN is defined to '2'. This makes it so that, after the ethernet header, the protocol header will be aligned on at least a 4-byte boundary. Nearly all of the IPV4 and IPV6 protocol processing assumes that the headers are properly aligned.

Let's now add some user data to the packet.



unsigned char *data = skb_put(skb, user_data_len); int err = 0; skb->csum = csum_and_copy_from_user(user_pointer, data, user_data_len, 0, &err); if (err) goto user_fault;


Layout of SKB after skb_reserve() This is what a new SKB looks like right after the user data is added.

skb_put() advances 'skb->tail' by the specified number of bytes, it also increments 'skb->len' by that number of bytes as well. This routine must not be called on a SKB that has any paged data. You must also be sure that there is enough tail room in the SKB for the amount of bytes you are trying to put. Both of these conditions are checked for by skb_put() and an assertion failure will trigger if either rule is violated.

The computed checksum is remembered in 'skb->csum'. Now, it's time to build the protocol headers. We'll build a UDP header, then one for IPV4.



struct inet_sock *inet = inet_sk(sk); struct flowi *fl = &inet->cork.fl; struct udphdr *uh; skb->h.raw = skb_push(skb, sizeof(struct udphdr)); uh = skb->h.uh uh->source = fl->fl_ip_sport; uh->dest = fl->fl_ip_dport; uh->len = htons(user_data_len); uh->check = 0; skb->csum = csum_partial((char *)uh, sizeof(struct udphdr), skb->csum); uh->check = csum_tcpudp_magic(fl->fl4_src, fl->fl4_dst, user_data_len, IPPROTO_UDP, skb->csum); if (uh->check == 0) uh->check = -1;


Layout of SKB after pushing UDP headers This is what a new SKB looks like after we push the UDP header to the front of the SKB.

skb_push() will decrement the 'skb->data' pointer by the specified number of bytes. It will also increment 'skb->len' by that number of bytes as well. The caller must make sure there is enough head room for the push being performed. This condition is checked for by skb_push() and an assertion failure will trigger if this rule is violated.

Now, it's time to tack on an IPV4 header.



	struct rtable *rt = inet->cork.rt;
	struct iphdr *iph;

	skb->nh.raw = skb_push(skb, sizeof(struct iphdr));
	iph = skb->nh.iph;
	iph->version = 4;
	iph->ihl = 5;
	iph->tos = inet->tos;
	iph->tot_len = htons(skb->len);
	iph->frag_off = 0;
	iph->id = htons(inet->id++);
	iph->ttl = ip_select_ttl(inet, &rt->u.dst);
	iph->protocol = sk->sk_protocol; /* IPPROTO_UDP in this case */
	iph->saddr = rt->rt_src;
	iph->daddr = rt->rt_dst;
	ip_send_check(iph);

	skb->priority = sk->sk_priority;
	skb->dst = dst_clone(&rt->u.dst);

Layout of SKB after pushing IP header This is what a new SKB looks like after we push the IPv4 header to the front of the SKB.

 

Just as above for UDP, skb_push() decrements 'skb->data' and increments 'skb->len'. We update the 'skb->nh.raw' pointer to the beginning of the new space, and build the IPv4 header.

This packet is basically ready to be pushed out to the device once we have the necessary information to build the ethernet header (from the generic neighbour layer and ARP).



Things start to get a little bit more complicated once paged data begins to be used. For the most part the ability to use [page, offset, len] tuples for SKB data came about so that file system file contents could be directly sent over a socket. But, as it turns out, it is sometimes beneficial to use this for nomal buffering of process sendmsg() data.

It must be understood that once paged data starts to be used on an SKB, this puts a specific restriction on all future SKB data area operations. In particular, it is no longer possible to do skb_put() operations.

We will now mention that there are actually two length variables assosciated with an SKB, len and data_len. The latter only comes into play when there is paged data in the SKB. skb->data_len tells how many bytes of paged data there are in the SKB. From this we can derive a few more things:

  • The existence of paged data in an SKB is indicated by skb->data_len being non-zero. This is codified in the helper routine skb_is_nonlinear() so that it the function you should use to test this.
  • The amount of non-paged data at skb->data can be calculated as skb->len - skb->data_len. Again, there is a helper routine already defined for this called skb_headlen() so please use that.
The main abstraction is that, when there is paged data, the packet begins at skb->data for skb_headlen(skb) bytes, then continues on into the paged data area for skb->data_len bytes. That is why it is illogical to try and do an skb_put(skb) when there is paged data. You have to add data onto the end of the paged data area instead.

 

Each chunk of paged data in an SKB is described by the following structure:

struct skb_frag_struct {
	struct page *page;
	__u16 page_offset;
	__u16 size;
};
There is a pointer to the page (which you must hold a proper reference to), the offset within the page where this chunk of paged data starts, and how many bytes are there.

 

The paged frags are organized into an array in the shared SKB area, defined by this structure:

#define MAX_SKB_FRAGS (65536/PAGE_SIZE + 2)

struct skb_shared_info {
	atomic_t dataref;
	unsigned int	nr_frags;
	unsigned short	tso_size;
	unsigned short	tso_segs;
	struct sk_buff	*frag_list;
	skb_frag_t	frags[MAX_SKB_FRAGS];
};
The nr_frags member states how many frags there are active in the frags[] array. The tso_size and tso_segs is used to convey information to the device driver for TCP segmentation offload. The frag_list is used to maintain a chain of SKBs organized for fragmentation purposes, it is _not_ used for maintaining paged data. And finally the frags[] holds the frag descriptors themselves.

 

A helper routine is available to help you fill in page descriptors.


void skb_fill_page_desc(struct sk_buff *skb, int i,
			struct page *page,
			int off, int size)

This fills the i'th page vector to point to page at offset off of size size. It also updates the nr_frags member to be one past i.

 

If you wish to simply extend an existing frag entry by some number of bytes, increment the size member by that amount.


With all of the complications imposed by non-linear SKBs, it may seem difficult to inspect areas of a packet in a straightforward way, or to copy data out from a packet into another buffer. This is not the case. There are two helper routines available which make this pretty easy.

First, we have:


void *skb_header_pointer(const struct sk_buff *skb, int offset, int len, void *buffer) 

You give it the SKB, the offset (in bytes) to the piece of data you are interested in, the number of bytes you want, and a local buffer which is to be used _only_ if the data you are interested in resides in the non-linear data area.

 

You are returned a pointer to the data item, or NULL if you asked for an invalid offset and len parameter. This pointer could be one of two things. First, if what you asked for is directly in the skb->data linear data area, you are given a direct pointer into there. Else, you are given the buffer pointer you passed in.

Code inspecting packet headers on the output path, especially, should use this routine to read and interpret protocol headers. The netfilter layer uses this function heavily.

For larger pieces of data other than protocol headers, it may be more appropriate to use the following helper routine instead.


int skb_copy_bits(const struct sk_buff *skb, int offset,
		  void *to, int len);

This will copy the specified number of bytes, and the specified offset, of the given SKB into the 'to'buffer. This is used for copies of SKB data into kernel buffers, and therefore it is not to be used for copying SKB data into userspace. There is another helper routine for that:
int skb_copy_datagram_iovec(const struct sk_buff *from,
			    int offset, struct iovec *to,
			    int size);

Here, the user's data area is described by the given IOVEC. The other parameters are nearly identical to those passed in to skb_copy_bits() above.

 


 

※ 출처 : http://vger.kernel.org/~davem/skb_data.html

posted by footprintz
2013. 10. 11. 15:14 개발 이야기/Linux Kernel
The kernel makes heavy use of inline functions. In many cases, inline expansion of functions is necessary; some of these functions employ various sorts of assembly language trickery that must be part of the calling function. In many other cases, though, inline functions are used as a way of improving performance. The thinking is that, by eliminating the overhead of performing actual function calls, inline functions can make things go faster.

The truth turns out not to be so simple. Consider, for example, this patch from Stephen Hemminger which removes the inline attribute from a set of functions for dealing with socket buffers ("SKBs", the structure used to represent network packets inside the kernel). Stephen ran some benchmarks after applying his patch; those benchmarks ran 3% faster than they did with the functions being expanded inline.

The problem with inline functions is that they replicate the function body every time they are called. Each use of an inline function thus makes the kernel executable bigger. A bigger executable means more cache misses, and that slows things down. The SKB functions are called in many places all over the networking code. Each one of those calls creates a new copy of the function; Denis Vlasenko recently discovered that many of them expand to over 100 bytes of code. The result is that, while many places in the kernel are calling the same function, each one is working with its own copy. And each copy takes space in the processor instruction cache. That cache usage hurts; each cache miss costs more than a function call.

Thus, the kernel hackers are taking a harder look at inline function declarations than they used to. An inline function may seem like it should be faster, but that is not necessarily the case. The notion of a "time/space tradeoff" which is taught in many computer science classes turns out, often, to not hold in the real world. Many times, smaller is also faster.

 

※ 출처 : http://lwn.net/Articles/82495/

 

posted by footprintz
2011. 10. 20. 11:43 개발 이야기/PHP

◈ file_exists()
 - 문제점 : safe mode 일 때, 올바른 결과값을 출력하지 않을 위험성
 - http://kr2.php.net/manual/kr/function.file-exists.php
 - http://codeigniter-kr.org/tip/view/546/page/2

◈ file_exists() 문제의 해결
 - is_file() 함수 사용

◈ safe mode
 - 정의 : PHP 안전 모드는 공유-서버 보안 문제를 풀려는 시도. 이 문제를 PHP 수준에서 풀려고 하는것은 구조적으로 올바르지 않음. 그러나 웹서버와 OS 수준에서의 차선책이 아주 현실적이지는 않기 때문에, 많은 사람, 특히 ISP, 들이 현재 안전 모드를 사용. (php 6.0 에서는 삭제)
 - 접근하는 디렉터리의 소유자나 소유그룹과 다를 때 문제가 발생
 - php.ini 에서 "safe_mode = On" 설정 시 동작
 - http://www.php.net/manual/kr/features.safe-mode.php
 - http://learder.tistory.com/827190
 - http://www.coldfusionfunnylog.com/blog/post.cfm/ubuntu-warning-safe-mode-restriction-in-effect

◈ safe mode 에 의해 제한/비활성화 되는 함수들
 - http://php.net/manual/kr/features.safe-mode.functions.php

◈ file_exists() 와 is_file() 사용시 주의점
 - 같은 파일 이름을 사용할 경우를 위해 함수 처리 결과를 캐시에 임시 저장하여 메모리의 일정 부분 자원으로 할당
 - clearstatcache() 사용하여 캐시 삭제
 - http://habony.tistory.com/35

◈ 처리 결과가 캐시 되는 함수들
 - stat(), lstat(), file_exists(), is_writable(), is_readable(), is_executable(), is_file(), is_dir(), is_link(), filectime(), fileatime(), filemtime(), fileinode(), filegroup(), fileowner(), filesize(), filetype(), and fileperms()

posted by footprintz
2011. 9. 26. 17:40 개발 이야기/C
◈ Test1.
for (i = 0; i < 64; i++) {
    test = 1 << i;
    debug(1, "[%2d] %llu\n", i, test);
}

◈ Test2.
for (i = 0; i < 64; i++) {
    test = 1LL << i;
    debug(1, "[%2d] %llu\n", i, test);
}

결과는?!
posted by footprintz
2011. 9. 5. 11:08 개발 이야기/Linux
posted by footprintz
2011. 9. 5. 10:09 개발 이야기/Linux Kernel
posted by footprintz
2011. 9. 4. 16:25 개발 이야기/Linux
  1. pid - Process id
  2. comm - The executable filename
  3. state - R (running), S(sleeping interruptable), D(sleeping), Z(zombie), or T(stopped on a signal).
  4. ppid - Parent process ID
  5. pgrp - Process group ID
  6. session - The process session ID.
  7. tty - The tty the process is using
  8. tpgid - The process group ID of the owning process of the tty the current process is connected to.
  9. flags - Process flags, currently with bugs
  10. minflt - Minor faults the process has made
  11. cminflt - Minor faults the process and its children have made.
  12. majflt
  13. cmajflt
  14. utime - The number of jiffies (processor time) that this process has been scheduled in user mode
  15. stime - in kernel mode
  16. cutime - This process and its children in user mode
  17. cstime - in kernel mode
  18. counter - The maximum time of this processes next time slice.
  19. priority - The priority of the nice(1) (process priority) value plus fifteen.
  20. timeout - The time in jiffies of the process's next timeout.
  21. itrealvalue - The time in jiffies before the next SIGALRM is sent to the process because of an internal timer.
  22. starttime - Time the process started after system boot
  23. vsize - Virtual memory size
  24. rlim - Current limit in bytes of the rss of the process.
  25. startcode - The address above which program text can run.
  26. endcode - The address below which program text can run.
  27. startstack - The address of the start of the stack
  28. kstkesp - The current value of esp for the process as found in the kernel stack page.
  29. kstkeip - The current 32 bit instruction pointer, EIP.
  30. signal - The bitmap of pending signals
  31. blocked - The bitmap of blocked signals
  32. sigignore - The bitmap of ignored signals
  33. sigcatch - The bitmap of catched signals
  34. wchan - The channel in which the process is waiting. The "ps -l" command gives somewhat of a list

 출처 : http://www.comptechdoc.org/os/linux/howlinuxworks/linux_hlproc.html

posted by footprintz
2011. 9. 4. 12:07 개발 이야기/CPU Arch
1. i386
 - Intel 의 80386 CPU 계열의 CPU

2. i586
 - Intel 의 80586 CPU 계열의 Pentium CPU

3. i686
 - Intel 의 80686 CPU 계열의 Pentium Pro 이상의 CPU

4. x86
 - x386, x586, x686 모든 플랫폼에서 동작

5. x86_64
 - 64비트 컴퓨터에서 동작하는 것을 의미합니다.

cf) 리눅스 배포판이나 패키지를 다운 받을 때 보면, i386, i586, x86, x86_64 등등 여러가지 가 있는 것을 볼 수 있는데각 소스 코드를 각 CPU 에 적합하게 컴파일한 결과를 다운 받아 사용할 수 있도록 한 것입니다.

출처 : http://blog.bagesoft.com
posted by footprintz
2009. 11. 29. 23:38 개발 이야기/Linux

$ sudo apt-get install xinetd
$ sudo apt-get install telnetd
$ vi /etc/xinetd.conf
###################################
service telnet
{
     disable = no
     flags = REUSE
     socket_type = stream
     wait = no
     user = root
     server = /usr/sbin/in.telnetd
     log_on_failure += USERID
}
###################################

$ sudo /etc/init.d/xinetd restart

cf) telnet root 접속
# vi /etc/pam.d/login 에서,
auth required  /lib/security/pam_securetty.so 이것을 주석처리

# sudo /etc/init.d/xinetd restart


########################################

telnet 터미널.


posted by footprintz
2009. 11. 26. 22:56 개발 이야기/C
sscanf는 문자버퍼로 부터 포맷 문자열을 입력을 받을때 사용하는 함수입니다.

line이라는 버퍼에 [service] 와 같은 입력문자열이 있을때 service만 받고자 한다면
sscanf(line, [%[^]], section);
과 같이 코딩을 합니다.

여기서 햇갈리는 부분이...[%[^]] 입니다...
'[' 다음에 오는 문자열중에서 ']'를 제외한 문자열( [  ^]  ] )을 찾겠다는겁니다...

[ 출처 : http://www.redef.pe.kr/431 ]

즉,

int seconds;
char message;

sscanf(line,"%d %64[^\n]",&seconds,message) // line 에서 읽어와서, 첫번째 숫자는 seconds에, 나머지 64문자(\n이 발견될때 까지의..)는 message에 저장.
posted by footprintz
2009. 11. 19. 19:48 개발 이야기/C
In C. (Embe)
원문 : http://blog.naver.com/iammaxg.do?Redirect=Log&logNo=6939391

먼저 다음에 물음에 답해보시라...
혹시 당신이 C로짠 임베디드 코드에서 다음과 같은 경우를 경험한적이 있는가??

* 옴티마이즈 옵션을 켜기 전까지는 코드가 잘 동작한다
* 어떤 인터럽트를 디스에이블(disable)시켜논 동안에는 코드가 잘 동작한다
* RTOS 가 탑재된 멀티태스킹 시스템에서 어떤 태스크(TASK)가 인에블(enable) 되기전까지는 태스크가 잘 동작한다..

만약 위의 물음에 "네(yes)"라고 대답한다면 그건 바로 당신이 volatile라는 C keyword를 사용하지 않았기 때문이다..
이건 비단 당신혼자만의 문제는 아니다..
많은 프로그래머들이 volatile 라는 키워드에 대해서 어설프게 잘못알고 있거나 제대로 사용하지 않고 있다..

이건 그리 놀랄만한 일이 아닌데 그건 바로 많은 C 책이 이점에 관해서 너무하리만큼 무심하기 때문이다..
volatile 는 변수를 선언할때 같이 사용하는 키워드다.
volatile를 사용함으로인해 컴파일러에게 volatile과 함께 선언된 변수는 언제 어느때든지 값이 바뀔수 있다는 것을 말한다.

구체적인 사용예를 들기전에 먼저 volatile에 대한 문법적인 사항을 알아보자..
volatile 변수를 사용하기 위해 volatile라는 키워드를 정의된 변수의 데이터 타입 앞 또는 뒤에 명시하면 된다..
다음과 같이 말이다

volatile int foo;
int volatile foo;

자, 그럼 포인터에서는 어떻게 될까??
포인터에서 뭔가 특별한점이 있다고 생각하는가??

천만의 말씀이다..
포인터라 해서 별반 다를게 없다..
포인터 역시 다음과 같이 하면된다..

volatile int *foo;
int volatile *foo;

그럼 이것역시 같다고 생각하는가??

int * volatile foo;
int volatile * volatile foo;
이건 숙제다..

마지막으로 volatile을 struct나 union에 적용시켜버리면 struct나 union의 모든 내용들은 volatile 이다..
만약 위와같은 경우를 원하지 않는다면 어떻게 할것인가??
struct나 union 멤버에게 개별적으로 사용하면 된다..
자, 그럼 본격적인 사용법을 알아보자...

어떤 변수들이 예고없이 값이 바뀔수 있을 가능성이 있는경우에는 volatile로 선언해야 한다.
사실상 다음의 3가지 타입의 변수들이 바뀔수 있다.

* memory-mapped periherral registers
* 인터럽트 서비스 루틴에 의해 수정되는 전역변수
* 멀티 태스킹 또는 멀티 쓰레드 에서 사용되는 전역변수

그럼 먼저 첫번째 항목에 대해서 좀더 자세히 알아보자.
임베디드 시스템에서는 진보되고 복잡한 실질적인 주변 디바이스(periherial) 를 포함하게 된다.
이런 peripherial들은 프로그램 흐름과 비동기적으로 값들이 변하는 레지스터들을 가지고 있는 경우가 대부분이다..
매우 간단한 예로 0x1234 address에 위치한 8비트 status 레지스터가 있다고 가정하고 생각해보자.
만약 이 레지스터가 0이 아닌 값을 가질때까지 이 레지스터를 폴링(polling)한다고 가정해보자.
그럼 당신은 분명히 다음과 같이 코드를 작성할것이다..

INT8U *ptr = (INT8U *)0x1234;

// wait for register to become non-zero
while (*ptr == 0);
// Do something else

만약 당신이 옴티마이즈 옵션을 켰다면 위의 코드는 제대로 동작하지 않을 확율이 굉장히 높다..
왜냐하면 컴파일러는 당신이 작성한 코드에 대해서 다음과 같은
어셈블러를 생성할것이다..
반드시 유심히 보길 바란다.. 중요하다..

move ptr, #0x1234
move a, @ptr
loop bz loop

자, 한번 분석해보자..
컴파일러는 굉장히 똑똑하게 어셈블리 코드를 생성한것을 볼수 있다.
첨에 한번반 0x1234를 억세스해서 값을 로딩한 이후로 두번다시는 0x1234를 억세스 하지 않는다.
두번째 코드에서 볼수 있듯이 값은 accumulator에 이미 로딩이 되있기 때문에 값을 재 로딩할 필요가 없다고 컴파일러는 판단하기 때문이다.
왜냐하면 값은 항상 같다고 보기 때문이다..
그러므로 3번째 라인에 의해 당신이 작성한 코드는 무한루프에 빠지게 된다.

정작 우리가 원하는 동작을 하기 위해서는 위의 코드를 다음과 같이 수정해야 한다.

INT8U volatile * ptr = (INT8U volatile *)0x1234;

그럼 컴파일러는 이제 다음과 같이 어셈블러를 생성할것이다..

mov ptr, #0x1234
loop mov a, @ptr
bz loop

자, 어떤가??
드뎌 당신이 원하는 결과를 얻게 될것이다...

자, 그럼 인터럽트 서비스 루틴의 경우에 대해서 생각해보자..
종종 인터럽트 서비스 루틴은 메인함수에서 테스트하는 변수를 셋팅하게 된다..
예를 들어 시리얼 포트 인터럽트는 각각에 수신한 캐릭터에 대해 ETX 캐릭터가 수신됬는지를 테스트한다고 가정해보자..
만약 ETX가 수신되면 인터럽트 서비스 루틴은 전역 플래그를 셋팅할것이다.

불완전한 코드를 다음에 보이겠다..

int ETXRcvd = FALSE;

void main (void)
{
...
while (!ETXRcvd) {
// what
}
...
}

interrupt void RxISR (void)
{
...
if (rx_char == ETX) {
ETXRcvd = TRUE;
}
...
}

옵티마이즈 옵션을 꺼논동안에는 코드가 올바르게 작동할 것이다..
그러나, 그렇지 않을 경우에는??
문제는 컴파일러는 ETXRcvd가 인터럽트 서비스 루틴에 의해서 값이 바꼈을 경우 이를 알수 없는 경우가 생긴다..
위에 peripherial 예세서 들었듯이 !EXTRcvd 는 항상 참이기 때문에 while 루프를 절대 벗어날수 없는 경우가 생길수도 있다..
게다가 심지어는 이런 이유로 인해 루프 이후에 코드들은 옵티마이즈에 의해 제거 되버릴수도 있다..
만약 당신이 운좋은 놈이라면 당신의 컴파일러는 이런 문제에 대해서 경고 메세지를 보내게 될것이다..
그렇지 않고 당신이 운좋은 놈이 아니거나 컴파일러가 제공하는 경고 메세지가 때로는 얼마나 무서운것인지를 경험해보지 못했다면 어떻게 될까??
말안해도 알리라 본다..
모른다면???

그건 나도 모르는 일이다...

이제 마지막으로 멀티 쓰레드또는 멀티 태스킹 어플리케이션 경우를 생각해 봐야 되는데 이 문제에 대해서는 여러분들의 호응이나 요청이 있을시 논의해 보도록 하겠다..
앞으로 우리가 임베디드 시스템 프로그램을 하면서 남용하지는 않더라도 volatile 키워드를 애용하도록 하자.
만약의 경우라도 대비해서 말이다.

임인건은 그의 저서 "터보씨 정복"에서 문제가 될 소지가 있는부분에 대해서는 할수만 있거든 미연에 사태를 막아놓고 아예 싹을 잘라버릴것을 심신 당부하고 있고 임베디드 프로그래밍 관련 서적 집필로 유명한 Michael Barr는 그이 저서 "Programming Embedded Systems in C and C++"에서 peripherial을 억세스 할때는 잔말 말고 무조건 volatile 키워드를 사용하라고 우리에게 협박(?)까지 하고 있다..

나의 작은 글이 여러분들의 임베디드 시스템 프로그래밍에 있어서 큰 도움이 되길 바란다..

####################################################################################
cf) In Java.
http://cleo230.egloos.com/2004885

posted by footprintz
2009. 11. 5. 16:22 개발 이야기/C

해쉬 테이블 또는 해쉬 맵은 key와 value를 갖는 자료 구조이다. 주요 동작은 효율적인 검색(주어진 키(예를 들어, 사람 이름)로 적합한 값을 찾는(전화번호)) 이다. 해쉬 함수를 이용해서 주어진 키를 해쉬값으로 변환하고 해쉬값을 인덱스로 하여 원하는 값이 있는 버켓(bucket)을 찾아내는 것이다.

1. 시간 복잡도와 해쉬 테이블의 용도

해쉬 테이블은 종종 연관 배열, set, 그리고 캐쉬의 구현에 사용된다. 배열과 비슷하게, 해쉬 테이블은 평균적으로 검색에 테이블 내 아이템의 수와 상관없이 O(1)의 상수 시간 복잡도를 제공한다. 그러나 최악의 경우 검색 시간은 O(n)으로 나빠질 수도 있다. 다른 연관 배열 자료 구조와 비교해, 해쉬 테이블은 많은 수의 자료가 저장될 때 좀 더 유용하며 데이터 셋의 크기를 예측할 수 있을 때 더욱 유용하다.

해쉬 테이블의 검색 성능은 해쉬 함수의 성능과 해쉬 테이블의 크기에 좌우된다. 충돌이 발생하면 할수록 성능은 점점 O(n)에 가까워지므로 충돌을 최대한 억제시키는 것이 해쉬의 핵심 포인트다.


2. 해쉬 함수

좋은 해쉬 함수는 해쉬 테이블 성능 향상에 필수이다. 질 낮은 해쉬 함수는 데이터들의 key를 같은 bucket으로 유도하는 경우가 많아짐으로써 충돌이 증가하고 이는 곧 심각한 해쉬 테이블의 성능 저하로 이어진다.(물론 어떤 구현으로도 충돌을 완전히 피할 수는 없다) 게다가 몇몇 해쉬 함수들은 계산 비용이 너무 많이 들어 상당히 부담스럽다.

좋은 해쉬 함수를 선택하는 것은 상당히 까다로운 일이다. 다른 알고리즘이나 자료구조와는 다르게 해쉬 함수에 대해서는 딱히 정형화된 기준이 없다. 아래에 정리할 해쉬 함수 파트는 3가지 조건 : simplicity, speed, strength 에 초점을 맞출 것이다.

단순성과 속도는 쉽게 측정이 가능하나 strength는 좀 더 애매한 개념이다. SHA-1 같은 cryptographic 해쉬 함수는 상대적으로 느슨한 strength가 필요하나 느린 속도나 복잡한 구조는 상당히 불만족스럽다. 사실 crytographic 해쉬 함수도 악의 있는 공격자에 대한 보호 장치를 제공하지 않아, 특정 해쉬 함수를 쓰기 보다 universal 해쉬 함수가 사용되어지는 경우도 있다.(Universal 해쉬 함수는 공격자에 의해 최악의 성능으로 빠질 수 없도록, 충돌이 너무 자주 일어나지 않도록 보장하는 해쉬 함수를 얘기한다)

표준 방식은 없지만, 대개 해쉬 함수가 avalanche effect를 낼 수 있는지 여부로 해쉬 함수의 strength를 측정한다.(avalanche(눈사태) effect : 입력 데이터를 살짝만 바꿔(ex. flipping single bit) 출력 데이터가 상당히 바뀌는 효과(ex. halfs the output bits flipping))

다행히 위의 3가지 조건을 만족시키는 좋은 해쉬 함수들이 나와 있으며 Jenkins의 One-at-a-time 해쉬 함수가 대표적인 예라 할 수 있다. 32/64비트 int형의 괜찮은 해쉬 함수를 첨부한다. ---> int_hash_func.txt

해쉬 함수를 작성하는 방법에 따라 종류를 분류해보면 분할, 폴딩, 중간-제곱 함수, 추출, 기수 변환 등이 있으며 일반적으로 해쉬 테이블의 크기가 소수(prime number)일 때 성능이 좋아진다.


3. 충돌 해결

두 개의 키가 같은 인덱스로 해싱되면 같은 곳에 저장될 수 없다. 따라서 해싱된 인덱스에 이미 다른 값이 들어 있다면, 새 데이터를 저장할 다른 위치를 찾은 뒤에야 저장할 수 있는 것이다. 100만개의 크기를 가지는 해쉬 테이블에 뛰어난 해쉬 함수로 가지고 있다고 해도, 대략 2500개의 레코드가 찼을 때 충돌이 발생활 확률은 95%에 이른다고 한다.

따라서 충돌 해결은 필수이며 가장 널리 쓰이는 chaining 방식과 open addressing 방식에 대해 알아보자.

1) Chaining

모든 bucket을 연결 리스트로 만들어 충돌이 발생하면 해당 bucket의 list에 추가하는 방식이다. 위 그림에서 보듯이, Smith와 Dee의 해쉬값이 873으로 동일하게 나왔으므로 Smith->Dee의 연결 리스트 형식으로 삽입하는 것이다.

Chaining 방식은 open-addressing 방식에 비해 크게 두 가지 이점을 가진다.

1. 삭제 작업이 간단하다.
2. 모든 버켓이 사용중이더라도 성능 저하가 더디게 나타나므로 open-addressing 방식에 비해 테이블 확장을 상당히 늦출 수 있다.

실제로 많은 chaining 해쉬 테이블에서 확장이 전혀 필요하지 않을 수 있는데, 테이블이 채워지는 것에 비례, 성능 저하가 linear하게 발생하기 때문이다. 예를 들어, chaining 해쉬 테이블에 적정 크기보다 2배의 데이터가 삽입되어도 2배의 속도 저하가 있을 뿐이다.

연결 리스트를 사용하는 chaining 해쉬 테이블은 연결 리스트의 단점도 그래도 물려받아, 작은 데이터들을 저장할 때 연결 리스트 자체의 오버헤드가 부담이 되고 traverse의 캐쉬 효율도 좋지 않다. 대체 자료구조로서 최악의 경우에 O(n)이 아닌 O(log n)의 검색 복잡도를 보장하는 self-balancing tree를 고려해 볼 필요도 있다.

그러나 해쉬 테이블이 꽉 찰 정도로 운영되거나 상당히 높은 확률로 충돌이 발생하지 않는다면(저렇게 되지 않도록 하는 것이 가장 좋다), 통상적으로 리스트의 길이는 대부분 상당히 짧으며 모든 버켓이 2개 이상의 개체를 가지지도 않기 때문에 연결 리스트를 이용한 chained 해쉬 테이블로도 충분히 효과적이다.

그리고 데이터의 크기가 작을 때는 space 오버헤드를 줄이고 캐쉬 효율을 높이기 위해 연결 리스트 대신 dynamic array를 쓸 수도 있다.

2) Open-addressing

Open-addressing 해쉬 테이블은 배열 내에 데이터를 바로 저장할 수 있다.

충돌은 탐사(probing) 방식으로 해결하며 다음과 같은 잘 알려진 probe sequence들이 있다.

1. Linear probing : 순차적으로 탐색하며 비어있는 버켓을 찾을 때까지 계속 진행된다. 최악의 경우, 탐색을 시작한 위치까지 돌아오게 되어 종료(모든 버켓 순회)하게 된다. 캐쉬의 효율은 높으나 데이터의 클러스터링에 가장 취약한 단점이 있다.

2. Quadratic probing : 2차 함수를 이용해 탐색할 위치는 찾는다. 캐쉬의 효율과 클러스터링에 강한 능력에서 linear probing과 double hashing probing의 중간 정도 능력을 가진다.

3. Double hashing probing : 하나의 해쉬 함수에 의해 충돌이 발생하면 2차 해쉬 함수를 이용해 새로운 주소를 할당하는 방법이다. 캐쉬 효율은 3가지 방식 중 가장 좋지 않지만, 클러스터링에 거의 영향을 받지 않는다. 또한 가장 많은 연산량을 요구한다.

탐사 방식에 따라 open-addressing 해쉬의 성능이 달라지지만, 가장 치명적인 영향을 미치는 요소는 바로 해쉬 테이블의 load factor(전체 슬롯에서 사용중인 슬롯 비율)이다. Load factor가 100%로 증가할수록 데이터를 찾거나 삽입하기 위해 필요한 탐사 횟수는 비약적으로(dramatically) 증가한다. 일단 테이블이 꽉 차게 되면 probing이 실패하여 끝나버리기도 한다. 아래 표는 load factor에 따른 평균 성공 탐색수와 실패수이다.

위 표와 같이 아무리 좋은 해쉬 함수를 쓰더라도 일반적으로 load factor는 80%로 제한된다.(자바에서의 Hashmap은 기본 load factor threshold가 75%이다) 클러스터링에 가장 취약한 linear probing 방식이 load factor가 높을수록 가장 급격하게 성능 저하가 발생하는 것을 확인할 수 있다. 따라서 load factor가 임계점을 넘어 큰 경우의 성능은

double hashing > quadratic > linear 의 순서다.

물론 질 낮은 해쉬 함수는 엄청난 클러스터링을 유발함으로써 아주 낮은 load factor에도 해쉬 테이블의 성능을 상당히 낮아지게 한다. 어떤 문제가 해쉬 함수의 클러스터링을 유발하는지 알기는 쉽지 않아도, 해쉬 함수가 심각한 클러스터링을 유발하게 하는 것은 상당히 쉽다. 그냥 잘 만들어진, 그리고 많은 사람들에 의해 충분히 검증된 공개된 함수들을 가져다 쓰자 :-)

3) Chaining vs Open-addressing

Chained 해쉬 테이블은 open-addressing에 비해 다음과 같은 장점을 가진다.

1. 효과적으로 구현하기 간단하고 기본적인 자료구조 정도만 요구된다.
2. 해쉬 함수를 구현(선택)하는 관점에서 볼 때, chained 해쉬 테이블은 클러스터링에 거의 영향을 받지 않아 충돌의 최소화만 중점적으로 살펴보면 된다. 반면에 open-addressing 방식은 클러스터링까지 피해야 하므로 해쉬 함수의 성능에 지대한 영향을 받아 해쉬 함수를 구현(선택)하기가 쉽지 않다.
3. 테이블이 채워져도 성능 저하가 linear하게 발생한다. 비록 테이블이 채워질 수록 chain은 늘어나겠지만(리스트의 길이가 길어지겠지만) near-filled 상태의 open-addressing 방식에서 발생하는 급작스런 lookup 시간의 증가는 발생하지 않는다. (아래 그림을 보자)
4. 데이터의 크기가 대략 5 words and more 이라면, open-addressing 방식보다 적은 메모리를 사용한다.
5. 테이블의 데이터가 산재해 있다면(아주 큰 배열에서 빈 공간이 많은), chained 해쉬 테이블은 연결 리스트 등 테이블 외 별도의 외부 저장 공간에 동적으로 할당하여 사용함으로써 2 ~ 4 words의 작은 데이터라도 미리 공간을 잡아놓고 사용하는 open-addressing 방식보다 적은 메모리를 사용한다.

작은 크기의 데이터에 대해(a few words or less) open-addressing은 chaining에 비해 다음과 같은 장점을 가진다.

1. Open-Addressing 방식은 어떠한 포인터도 저장할 필요가 없고 테이블 외부에 어떠한 추가적인 저장 공간이 필요 없으므로 chaining 방식보다 메모리 효율이 높다.
2. 삽입시 메모리 할당 오버헤드가 없으며, 메모리 할당자가 없이도 구현이 가능하다.
3. 외부에 별도 공간을 필요로 하지 않기 때문에 chaining의 연결 리스트 같은 외부 공간에 필요한 추가적인 작업이 요구되지 않는다. 또한 (특히 linear probing에서) chaining보다 뛰어난 locality of reference(하나의 자원에 여러 번 접근하는 process)를 가진다. 데이터의 크기가 작다면, 특히 lookup에서, 이러한 특성들로 인해 chaining보다 성능이 좋을 수 있는 것이다.
4. 포인터를 사용하지 않음으로써 serialization이 용이하다.

반면에 open-addressing 방식은 큰 데이터를 다뤄야 할 때는 좋지 않은 선택인데, 데이터들이 캐쉬 라인을 채워버릴 것이며(캐쉬의 이득이 없어짐) 많은 공간이 (크기가 큰) 빈 슬롯들에 의해 낭비될 것이다.

정리하자면 open-addressing 방식은 테이블에 모두 저장될 수 있고 캐쉬 라인에 적합할 수 있을 정도로 데이터의 크기가 작을수록 성능이 더 좋아진다. 테이블의 높은 load factor가 예상되거나, 데이터가 크거나, 데이터의 길이가 가변일 때 chained 해쉬 테이블은 open-addressing 방식보다 적어도 동등하거나 훨씬 더 뛰어난 성능을 보인다.

4) Coleasced hashing

Chaining과 open-addressing을 혼합한 방식이며, 테이블 내의 버켓들끼리 서로 chain 링크를 가지게 한다.(Chained 방식은 각 버켓들 마다 고유의 chain 링크를 갖는다)

Open-addressing 처럼, chaining에 비해 storage usage and cache에서 우위를 보인다.
Chaining 처럼, 클러스터링에 거의 영향을 받지 않으며 실제로 테이블이 높은 밀도로 효과적으로 채워질 수 있다.
하지만 chaining과 다르게 버켓끼리 chain 링크를 거는 방식이므로 테이블 슬롯보다 많은 수의 데이터를 저장할 수 없다.


4. Table resizing

성능이 좋은 해쉬 함수를 사용하면 일반적으로 전체 슬롯의 70~80%가 채워져도 해쉬 테이블의 성능은 유지된다. 이 상태에서 더 많은 데이터가 추가되면, 충돌 해결 메커니즘에 따라 gradually or dramatically 하게 성능 저하가 발생한다. 이를 피하기 위해 load factor가 특정 임계점을 돌파하면 더 큰 테이블을 새로 만들어 데이터를 모두 옮기는 방법이 있다.

테이블 확장은 상당히 비용이 많이 드는 작업이며, 확장의 필요성은 해쉬 테이블의 단점 중 하나이다. 아주 무식하게 데이터가 하나씩 삽입될 때마다 매번 테이블을 늘어난 크기만큼 확장시킨다고 가정하면, 성능이 기하급수적으로 떨어질 것이며 더 이상 사용하기 힘든 해쉬 테이블이 될 것이다. 하지만 특정 퍼센트(예를 들어 10%, 100%)로 확장을 한다고 하면, 확장이 자주 발생하지 않아 lookup에 발생하는 평균적인 시간이 O(1)으로 되는 amortized(평소엔 좋지만 최악의 상황도 있는) 복잡도를 갖게 된다.

반면에 특히 real-time 시스템 같은 시스템에서는 테이블을 확장시키는 발생하는 비용을 감당하지 못할 수 있다. 이를 간단하게 해결하려면, 삽입될 데이터의 수보다 아주 큰 테이블을 만들거나 삽입 자체를 너무 많이 못하게 금지시키면 된다. 또 다른 방법으로 유용하지만 more memory-intensive인 기술은 다음과 같이 점진적으로 확장하는 방식이다.

1. 새 해쉬 테이블을 할당하지만 이전 해쉬 테이블을 삭제하지 않고, lookup 과정에서 두 테이블 모두 체크한다.
2. 데이터가 삽입될 때마다 새 테이블에 저장하고 특정 k 만큼 이전 테이블에서 새 테이블로 데이터를 이전한다.
3. k 만큼 데이터를 옮기다가 이전 테이블에 저장된 데이터가 없으면 이전 테이블을 free 한다.

Linear hashing은 incremental 해쉬 테이블 확장을 가능하게 하는 해쉬 테이블 알고리즘이다. 하나의 해쉬 테이블로 구현되지만, 두 개의 유효한 lookup 함수를 가진다.

아무리 훌륭한 방법을 고안, 구현하더라도 확장은 분명 해쉬 테이블의 심각한 성능 저하를 초래할 것이다. 가급적 확장이 일어나지 않도록 해쉬 테이블을 설계하고, 필요하다면 load factor가 일정 이상 늘어나지 않도록 테이블의 데이터 수를 제한하는 것도 확장하는 것보다는 낫다고 생각한다.


5. 해쉬의 단점

해쉬 테이블은 데이터를 pseudo-random 위치에 저장하기 때문에, 데이터를 정렬된 순서로 접근하는 것에 엄청난 비용이 발생한다. Self-balancing binary tree 같은 다른 자료구조에서는 일반적으로 lookup 시간이 O(log n)으로 느리고 구현도 더 복잡하지만 항상 데이터가 정렬되어 있다. 그리고 traverse 능력도 현저히 떨어지는데, 데이터가 산재해 있을 확률이 높은 해쉬 테이블의 특성상 빈 슬롯도 모두 체크하면서 순회를 해야 하기 때문이다.

해쉬 테이블은 일반적으로 locality-of-reference가 취약한데 데이터의 접근 패턴이 기본적으로 해쉬값을 이용한 jump around 방식이기 때문이며, 이는 프로세서의 캐쉬 미스를 발생(cause long delay)시킨다. 데이터의 수가 적고 key type이 integer 처럼 비교하기에 비용이 적게 든다면, linear search를 하는 배열 같은 간단한 자료구조에서 lookup에 더 나은 성능을 보인다.(일반적으로 hash는 개체수가 적을 때 성능이 떨어진다)

또한 해쉬 테이블은 구현 및 사용이 좀 더 어렵고 문제를 수반하기 쉽상이다. 모든 key type에 대해 강력한 해쉬 함수를 요구하는데, 다른 자료 구조의 간단한 비교 함수에 비해 설계하기도 구현하기도 디버그하기도 쉽지 않다. 특히 open-addressing 방식에서는 질 낮은 해쉬 함수를 만들기가 꽤나 쉽다.


6. 결론

하지만 최근 Java 등에서 map이 hashmap으로 대체되는 등, 검색이 주요 동작인 자료구조의 핵심이 되고 있다. 트리의 높이에 탐색 시간이 비례하는 map에 비해 평균적으로 O(1)의 탐색 시간을 보장하는 hashmap이 훨씬 좋음은 두 번 말해 입 아플 정도다.

Hash는 구현하기 어려운 자료구조가 아니나, 아름답게 구현하기는 무척 어려운 편이다. 뛰어난 해쉬 함수를 구현하거나 찾고 원만한 충돌 해결 알고리즘을 사용할 수 있다면 hash는 반드시 강력한 도구가 될 것이다.

[출처] : http://sweeper.egloos.com/925740

posted by footprintz
2009. 11. 5. 16:16 개발 이야기/C

■ 소켓모드
소켓은 소켓 함수 호출시 동작 방식에 따라 블로킹과 넌블로킹 소켓으로 구분하며 이를 소켓모드라고 부른다.


○ 블로킹 소켓
소켓 함수 호출 시 조건이 만족되지 않으면 함수는 리턴하지 않고 해당 스레드는 대기 상태가 된다.
소켓 함수는 리턴하지 않으므로 멀티스레드를 사용하여 다른 작업을 하지 않는 한 애플리케이션이 더는 진행할 수 없다.
socket() 함수는 기본적으로 블로킹 소켓이다.


○ 넌블로킹 소켓
소켓 함수 호출 시 조건이 만족되지 않더라도 함수가 리턴하므로 해당 스레드는 계속 진행 할 수 있다.
ioctlsocket()함수를 호출해야만 넌블로킹 소켓으로 바꿀 수 있다.

SOCKET  listen_sock = socket( AF_INET, SOCK_STREAM, 0 );\
u_long on = TRUE;
retval = ioctlsocket( listen_sock, FIONBIO, &on );

넌블로킹 소켓은 WSAGetLastError()함수를 호출하여 반드시 오류코드를 확인해야 한다.
WSAEWOULDBLOCK() -> 넌블로킹 소켓을 사용할 경우의 오류 코드.
이는 만족하지 않고 넘어간 것이므로 나중에 다시 소켓 함수를 호출하면 된다.

client_sock = accept( listen_sock, ( SOCKADDR* )&clientaddr, &addrlen );
if( client_sock == INVALID_SOCKET )
{
    
if( WSAGetLastError() != WSAEWOULDBLOCK ) {
                 //   에러 처리
    }
    
continue;
}

넌블로킹 소켓에서는 다음과 같이 WSAEWOULDBLOCK를 항시 체크하여 다시 소켓함수를 호출하여야 한다.
※ listen_sock가 넌블로킹이면 client_sock도 넌블로킹 소켓이 된다.

■ 넌블로킹 소켓의 특징
장점 : 소켓 함수 호출 시 블록되지 않으므로 다른 작업을 진행 할 수 있다.
         멀티스레드를 사용하지 않고 여러 개의 입출력을 처리 할 수 있다.

단점 : 소켓 함수를 호출할 때마다 WSAEWOULDBLOCK등 오류 코드를 확인하고, 
         다시 해당 함수를 호출해야하므로 프로그램 구조가 복잡해진다.
         블로킹 소켓을 사용한 경우보다 CPU사용률이 높다.


■ 이상적인 소켓 입출력 모델
1. 모든 Client가 접속이 성공한다.

2. Server는 각 Client의 서비스 요청에 최대한 빠르게 반응하며 고속으로 데이터를 전송한다.

3. 위와 같은 기능을 제공하되 시스템 자원 사용량을 최소화한다. 즉 CPU 사용률이나 메모리 사용량등을 최소화한다.

■ 소켓 입출력 모델에 요구되는 사항
1. 소켓 함수 호출 시 블로킹을 최소화 한다.
    CPU사용률을 최소한으로 하면서 넌블로킹 소켓을 사용한 모든 소켓 함수 호출을 성공시켜야 한다.

2. 입출력 작업을 다른 작업과 병행한다.

3. Thread 개수를 최소화한다.
    Thread를 생성할 때마다 1MB의 메모리가 할당된다. 
    시스템 내의 Thread 수가 많아질수록 각 Thread의 응답속도는 느려지므로 결과적으로 Server의 성능 저하를 불러온다.
    -> Context switching이 자주 발생하므로 늦어진다.

4. 유저모드와 커널모드 전환 횟수와 데이터 복사를 최소화 한다.
    유저모드와 커널모드의 전환은 상당한 CPU 사이클을 소모한다. 

posted by footprintz
2009. 7. 20. 15:58 개발 이야기/CPU Arch

레지스터
처음에 인텔 80386은 32비트 일반 레지스터 8개와 부동 소수점 실수 레지스터 8개를 지원했다. 뒤에 나온 프로세서들은 MMX, 3DNow!, SSE, SSE2, SSE3과 같은 다양한 SIMD 명령 집합들을 위한 레지스터들을 더 지원한다.
또한, 응용프로그램에 의해 사용되지는 않고 운영체제에 의해 빈번하게 사용되는 시스템 레지스터들이 존재한다. segment, control, debug 그리고 test 레지스터가 그들이다. 여섯개의 segment 레지스터는 주로 메모리 관리에 주로 사용되며, control, debug, test 레지스터는 모델에 따라 다양하다.

범용 레지스터
엄밀히 말하면 x86 아키텍처의 범용 레지스터는 이름과는 다르게 일부 특수한 기능을 가지고 있다. 이들 범용 레지스터들은 보통 자유롭게 대입이나 연산이 가능하긴 하지만 특정 명령에서는 하나 또는 두 개의 레지스터만을 쓸 수 있는 경우가 상당수 존재하며, 데이터를 다루는 레지스터와 주소를 다루는 레지스터가 서로 분리되어 있다. 또한 많은 수의 명령들이 레지스터에 메모리의 내용을 따로 옮기지 않고도 연산을 수행할 수 있다.
이런 특이한 구현은 1970년대의 관습에서 이어진 것으로, 후대의 아키텍처들과 IA-32 확장(예를 들어 AMD64의 64비트 확장)에서는 모든 범용 레지스터는 모든 명령에서 사용할 수 있도록 되어 있다. 물론 이는 IA-32에 영향을 끼치지는 않았다.

일반 레지스터
32비트 일반 레지스터는 4개가 있다. 이들은 주소 레지스터보다는 더 일반적인 용도로 쓰이지만, 특정 명령에서는 특정한 레지스터만 쓸 수 있는 경우가 있다.

EAX: 누산기에 해당하며 대부분의 연산들이 수행된다.
ECX: 개수, 횟수 등을 저장하는 카운터로 주로 사용된다.
EDX: 누산기의 확장으로 누산기와 관련된 연산에서 사용된다. EDX와 EAX를 합쳐 64비트 연산을 하는 경우가 대표적이다.
EBX: 일반적인 데이터 레지스터로, 원래 16비트 모드에서는 포인터로 사용했다.

8비트 및 16비트 레지스터
8비트 및 16비트의 레지스터도 역시 사용이 가능하다 예를 들어 32비트 EAX 레지스터의 하위 16비트는 AX 레지스터라는 이름으로 접근이 가능하다. 16비트 레지스터 중 일부는 32비트 레지스터의 경우와 마찬가지로 하위의 8비트로 분할할 수 있다. 16비트 레지스터의 상위 8비트를 AH 레지스터라고 불리며 하위 8비트를 AL레지스터라고 한다. 이와 유사하게 EBX 레지스터도 BX(16비트), BH(상위 8비트)와 BL(하위 8비트로)나뉠 수 있다.

주소 레지스터
32비트 일반 주소 레지스터는 4개가 있으며, 일반 레지스터에 속하지 않는 주소 레지스터 EIP도 있다.

ESP (stack pointer): 스택의 꼭대기 주소를 담는다.
EBP (base pointer): 현재 스택 프레임의 주소를 담으며, 일반적인 목적으로 쓰기도 한다.
ESI (source index): 문자열 연산에서 사용되는 원본 주소를 담는다.
EDI (destination index): 문자열 연산에서 사용되는 목적 주소를 담는다.
EIP (instruction pointer): 현재 실행되는 명령의 주소를 담는다.

[출처 : Wiki]
posted by footprintz
2009. 5. 21. 21:24 개발 이야기/C
CWinAppEx VS2008 Sp1설치 후 
MFC project를 생성하면 Wizard가 생성하는 code중 약간 바뀐 부분이 있습니다.

App는 CWinAppEx(기존에는 CWinApp를 상속)를 상속받고
stdafx.h에 #include <afxcontrolbars.h>가 추가됩니다.

그런데 문제는 VS2008 sp1에서 생성한 project를
VS2008 sp1이 설치되지 않은 computer에서 compile할 경우
Error가 발생한다는 점입니다.
stxafx.h에 afxcontrolbars.h를 찾을 수 없어 Compile Error가 발생합니다.

이 때는 간단히 수정할 수 있습니다.
1. CWinAppEx를 CWinApp로 고치고
2. #include <afxcontrolbars.h>를 삭제해줍니다.

출처 : Developer's Page
posted by footprintz
2009. 3. 31. 05:55 개발 이야기/C

리눅스에서는 itoa라는 함수가 인식되지 않습니다. 
int형 변수의 값을 char 배열에 넣는 함수를 사용하고 싶다면

sprintf함수가 사용가능하시다면 대체해 보세요.
sprintf(버퍼,"형식지정자", 값) 입니다.

예를들어,
int i=4;
char buf[100];
sprintf(buf, "%d", i);
라고 하면 버퍼에 문자열 4가 들어갑니다.

posted by footprintz
2009. 1. 19. 15:37 개발 이야기/Linux

[한글 설정]
1. 나비를 설치한다.
$ sudo apt-get install nabi

2. 디렉토리에 sudo vi .gnomerc 하여 아래와 같이 입력했다.
export LANG=ko_KR.UTF-8
export LC_ALL=ko_KR.UTF-8
export XMODIFIERS="@im=nabi"
export GTK_IM_MODULE=hangul3f
export GDK_USE_XFT=1
nabi &

3. $ sudo dpkg-reconfigure locales

4. 이곳에서 영문메뉴에서 입력만 한글로 사용할려면 en_US.UTF-8 UTF-8 를 선택하고, 모든 메뉴를 한글로 할려면 ko_KR.EUC-KR EUC-KR, ko_KR.UTF-8 UTF-8 를 선택한다.

5. 재부팅.
재부팅하고나면 나비가 뜰것이다. 이후 간단한 설정만 해주면 잘 나온다.
자세한 것은 https://wiki.ubuntu.com/KoreanSetupHowto?highlight=%28korean%29/ 를 참고


ps.1 : 2006년 3월 9일 추가
Ubunt Dapper 에서는 locale 정책이 바뀌었다. 이전까지는 sudo dpkg-reconfigure locales 명령어로 locale을 수정할수 있었으나 이제는 인스톨시 한국어를 선택했다면 단지 영어와 한국어의 두 종류로 선택이 될뿐이다. 즉, UTF-8로만 선택이 된다. EUC-KR을 선택하려면 수동으로 변경해주어야 한다.

Ubuntu Dapper(Fight 4) AMD64(2006년 3월 9일 현재 커널 2.6.15-17-AMD64-generic)에서 실행.
1. $ sudo vim /etc/environment

2.
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11:/usr/games"
LANG="ko_KR.UTF-8"
LANGUAGE="ko_KR:ko:en_GB:en"

위에와 같이 선택이 되어있다. EUC-KR를 추가해주자.

PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11:/usr/games"
LANG="ko_KR.UTF-8"
LANG="ko_KR.EUC-KR"
LANGUAGE="ko_KR:ko:en_GB:en"

3. 재부팅후 EUC-KR 로 된 사이트나 마운트된 윈도등을 보면 잘 보인다.^^

[vi 설정]
기본 우분투 설치했을때, vim-tiny 버젼이 설치됨.
지원되는 명령어가 제한적이므로 전체설치를 해야함.
#apt-get install vim
vi .vimrc 로 아래 명령 추가
set number
set backgruound=dark or light // 바탕이 흑백일 경우 잘보이기
set shiftwidth=3 // 자동 맞추는 간격
set tabstop=3
set ruler
set visualbell // 틀렸을때 소리대신 깜박임
set textwidth=79
set autoindent
set cindent
set ai // 자동행
set si // if 문 다음에 자동으로 맞추어준다.
syntax on

관련링크 : http://blog.naver.com/eajnis?Redirect=Log&logNo=100056467138

[vi 키]
관련링크 : http://blog.naver.com/cute772?Redirect=Log&logNo=60005425106

[vm ware 비프음 설정]
linux 비프음 없애기

#vi ~/.bashrc
setterm -blength 0
xset -b

[default root password modify]
$sudo apt-get update

$ sudo sh
Password:
sh-3.1#

$ sudo passwd
Password:
Enter new UNIX password:
Retype new UNIX password:

posted by footprintz
2009. 1. 15. 09:38 개발 이야기/Network

#include<stdio.h>
#include<stdlib.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
int main(int argc, char** argv)
{
//     char *serv_port = "8080";
        char *str;
        struct sockaddr_in addr_ip_address;
        socket(PF_INET,SOCK_STREAM,0);
        memset(&addr_ip_address, 0, sizeof(addr_ip_address));
        addr_ip_address.sin_family = AF_INET;

        addr_ip_address.sin_addr.s_addr = htonl(INADDR_ANY);
        printf("INADDR_ANY : %s\n",INADDR_ANY);
        str = inet_ntoa(addr_ip_address.sin_addr);

        printf("%s\n", str);
        return 0;
}

========================================================================================[judgementday@localhost network_programming]$ ./ip_address
INADDR_ANY : (null)
0.0.0.0
========================================================================================

INADDR_ANY 은 localhost인데 자신의 IP가 아닌 ox0000~으로 되어있다. INADDR_ANY로 주소를 잡아주면 커널(OS)이 그 주소를 잡아주는게 아니라 0.0.0.0으로 대기중에 있다가 connect(클라이언트)에서 특정 서버로 접속하면 소켓이 바인드 된다.

한동안 손 놓고 있다가 다시 볼려니 뭔지 잘 모르겠다.
이런, 바보!! ㅠ

[출처 : http://blog.naver.com/kernoarsid02]

posted by footprintz
prev 1 next