😎 κ³΅λΆ€ν•˜λŠ” μ§•μ§•μ•ŒνŒŒμΉ΄λŠ” μ²˜μŒμ΄μ§€?

[λ¦¬λˆ…μŠ€ λ„€νŠΈμ›Œν¬ ν”„λ‘œκ·Έλž˜λ°] 병행 처리 μ„œλ²„ λ³Έλ¬Έ

πŸ‘©‍πŸ’» IoT (Embedded)/Raspberry Pi

[λ¦¬λˆ…μŠ€ λ„€νŠΈμ›Œν¬ ν”„λ‘œκ·Έλž˜λ°] 병행 처리 μ„œλ²„

μ§•μ§•μ•ŒνŒŒμΉ΄ 2024. 1. 31. 13:01
728x90
λ°˜μ‘ν˜•

<μ„œμ˜μ§„ λ‹˜μ˜ 사물인터넷을 μœ„ν•œ λ¦¬λˆ…μŠ€ ν”„λ‘œκ·Έλž˜λ° with λΌμ¦ˆλ² λ¦¬νŒŒμ΄ μ„œμ μ„ μ°Έκ³ ν•΄μ„œ μž‘μ„±ν•˜μ˜€μŠ΅λ‹ˆλ‹€ :-)>

 

⭐ 병행 μ²˜λ¦¬ μ„œλ²„

μ•žμ„œ μ„œλ²„ μ½”λ“œλ₯Ό λΉŒλ“œν•œ ν›„ 2λŒ€μ˜ ν΄λΌμ΄μ–ΈνŠΈκ°€ μ ‘μ†ν•œ 경우라면

첫 번째 ν΄λΌμ΄μ–ΈνŠΈλŠ” μ„œλ²„μ— λ¬Έμ œμ—†μ΄ μ ‘μ†λ˜μ§€λ§Œ,

두 번째 ν΄λΌμ΄μ–ΈνŠΈκ°€ μ ‘μ†ν•˜λ©΄, 두 번째 ν΄λΌμ΄μ–ΈνŠΈλŠ” 첫 번째 ν΄λΌμ΄μ–ΈνŠΈμ˜ 연결이 λŠμ–΄μ§ˆ λ•ŒκΉŒμ§€ λŒ€κΈ° μƒνƒœλ‘œ μž‡κ»˜ λœλ‹€

 

μ—¬λŸ¬ ν΄λΌμ΄μ–ΈνŠΈλ₯Ό λ™μ‹œμ— λ³‘λ ¬λ‘œ μ²˜λ¦¬ν•˜κΈ° μœ„ν•΄μ„œλŠ” λ©€ν‹° ν”„λ‘œμ„ΈμŠ€λ‚˜ λ©€ν‹° μŠ€λ ˆλ“œ 기반의 μ„œλ²„κ°€ ν•„μš”ν•˜λ‹€

λ©€ν‹° ν”„λ‘œμ„ΈμŠ€ μ„œλ²„μ˜ 경우 ν΄λΌμ΄μ–ΈνŠΈμ˜ 접속이 λ“€μ–΄μ˜¬ λ•Œλ§ˆλ‹€ μƒˆλ‘œμš΄ ν”„λ‘œμ„ΈμŠ€λ₯Ό λ§Œλ“€μ–΄μ„œ μ²˜λ¦¬ν•  수 μžˆλ‹€

BUT 🚨 ν”„λ‘œμ„ΈμŠ€λ₯Ό λ§Œλ“œλŠ”λ° μ‹œκ°„μ΄ 많이 걸리고, ν”„λ‘œμ„ΈμŠ€ κ°„ 톡신 (IPC) 이 λ©€ν‹° μŠ€λ ˆλ“œμ— λΉ„ν•΄ λ³΅μž‘ν•˜κΈ° λ•Œλ¬Έμ— μš”μ¦˜μ€ λ©€ν‹° μŠ€λ ˆλ“œλ₯Ό 더 많이 μ“΄λ‹€

 

λ©€ν‹° μŠ€λ ˆλ“œ μ„œλ²„λ„ ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ΄ λ“€μ–΄μ˜¬ λ•Œλ§ˆλ‹€ μƒˆλ‘œμš΄ μŠ€λ ˆλ“œλ₯Ό λ§Œλ“€μ–΄μ„œ μ²˜λ¦¬ν•œλ‹€

λ©€ν‹° ν”„λ‘œμ„ΈμŠ€λ³΄λ‹€ μ‰½κ²Œ μŠ€λ ˆλ“œλ₯Ό λ§Œλ“€ 수 있고, μ„œλ‘œ 톡신이 κ°„λ‹¨ν•˜κΈ° λ•Œλ¬Έμ— 많이 μ‚¬μš©λœλ‹€

 

닀쀑 ν΄λΌμ΄μ–ΈνŠΈμ˜ 톡신을 μ²˜λ¦¬ν•˜λŠ” λ°©λ²•μœΌλ‘œ λ©€ν‹° ν”„λ‘œμ„ΈμŠ€μ™€ λ©€ν‹° μŠ€λ ˆλ“œ 방식 이외에도 I/O λ©€ν‹° ν”Œλž™μ‹± (Multiplexing) 방식이 μžˆλ‹€

λ©€ν‹° ν”Œλž™μ‹± 방식은 ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”κ΅¬μ‚¬ν•­μ΄ λ§Žμ§€ μ•Šμ€ 경우 주둜 μ΄μš©λ˜λŠ”λ°, μ΄λŠ” ν•˜λ‚˜μ˜ μ†ŒμΌ“ λ””μŠ€ν¬λ¦½ν„Έλ₯΄ 효율적으둜 μ‚¬μš©ν•  수 있게 ν•΄μ€€λ‹€

I/O λ©€ν‹° ν”Œλž™μ‹±μ€ ν•˜λ‚˜μ˜ ν”„λ‘œμ„ΈμŠ€ λ˜λŠ” μŠ€λ ˆλ“œμ—μ„œ μ‚¬μš©ν•˜λŠ” 닀쀑 처리 λ°©λ²•μœΌλ‘œ κ΅¬λΆ„λœλ‹€

ꡬ뢄 λ‚΄μš© λΉ„κ³ 
폴링 (Polling) 루프λ₯Ό μ΄μš©ν•΄μ„œ μ²˜λ¦¬ν•΄μ•Ό ν•  일이 μžˆλŠ”μ§€ 주기적으둜 μ§€μΌœλ³΄λ‹€κ°€ μ²˜λ¦¬ν•΄μ•Ό ν•  μž‘μ—…λ“€μ΄ 생기면 순차적으둜 λŒμ•„κ°€λ©΄μ„œ μ²˜λ¦¬ν•˜λŠ” 방법 poll
μ…€λ ‰νŠΈ (Selecting) μ§€μ •λœ 파일 λ””μŠ€ν¬λ¦½ν„°μ˜ λ³€ν™”λ₯Ό ν™•μΈν•΄μ„œ λ³€ν™” (μž…λ ₯, 좜λ ₯, μ—λŸ¬)κ°€ κ°μ§€λ˜λ©΄ ν•΄λ‹Ή μž‘μ—…μ„ μ²˜λ¦¬ν•˜λŠ” 방법 select
μΈν„°λŸ½νŠΈ (Interrupt) ν”„λ‘œμ„ΈμŠ€κ°€ μ–΄λ–€ μž‘μ—…μ„ μ²˜λ¦¬ν•˜λŠ” 도쀑에 νŠΉμ •ν•œ μ΄λ²€νŠΈκ°€ λ°œμƒν•˜λ©΄ μ‹œκ·Έλ„ 등을 μ΄μš©ν•΄μ„œ ν•΄λ‹Ή 이벀트λ₯Ό μ²˜λ¦¬ν•˜λŠ” 방법 μ‹œκ·Έλ„, epoll

 

폴링 (polling) 방식은 컀널이 μ£Όλ³€ μž₯μΉ˜μ—κ²Œ 일을 μ‹œν‚¨ ν›„ 루프λ₯Ό λŒλ©΄μ„œ κ·Έ 일이 μ™„λ£Œλ˜μ—ˆλŠ”μ§€ 주기적으둜 검사

μΈν„°λŸ½νŠΈ (interrupt) 방식은 μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ μ£Όλ³€ μž₯μΉ˜μ—κ²Œ 일을 μ‹œν‚¨ ν›„ ν”„λ‘œμ„ΈμŠ€λŠ” λ‹€λ₯Έ 일을 ν•˜λ©° λ””λ°”μ΄μŠ€κ°€ 일을 μ™„λ£Œν•œ ν›„ μ‹œκ·Έλ„μ΄λ‚˜ λ‹€λ₯Έ μΈν„°λŸ½νŠΈλ‘œ μ•Œλ¦¬λŠ” 방식

select 와 poll 은 파일 λ””μŠ€ν¬λ¦½ν„°λ₯Ό 기반으둜 μ²˜λ¦¬ν•œλ‹€

select 은 μ΅œλŒ€ 1024 개의 파일 λ””μŠ€ν¬λ¦½ν„°λ§Œ μ²˜λ¦¬ν•  수 μžˆμ§€λ§Œ, poll 은 더 λ§Žμ€ 파일 λ””μŠ€ν¬λ¦½ν„°λ₯Ό μ²˜λ¦¬ν•  수 μžˆλ‹€

 

select와 poll이 λͺ¨λ‘ 이벀트λ₯Ό κ°μ§€ν•˜κΈ° μœ„ν•΄ 파일 λ””μŠ€ν¬λ¦½ν„°λ₯Ό 순차적으둜 κ²€μƒ‰ν•˜κ³ , μ΄λ²€νŠΈκ°€ λ°œμƒν•˜λ©΄ 파일 λ””μŠ€ν¬λ¦½ν„°μ˜ 집합(fd_set)을 λͺ¨λ‘ λ³€κ²½ν•΄μ•Ό ν•˜κΈ° λ•Œλ¬Έμ— 속도가 λŠλ¦¬λ‹€

 

μ΄λŸ¬ν•œ 문제λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ λ§Œλ“€μ–΄μ§„ 것이 epoll

epoll 은 파일 λ””μŠ€ν¬λ¦½ν„° 수의 μ œν•œμ„ 받지 μ•ŠμœΌλ©°, λ¦¬λˆ…μŠ€ μ»€λ„μ—μ„œ 파일 λ””μŠ€ν¬λ¦½ν„°λ₯Ό κ΄€λ¦¬ν•˜λ―€λ‘œ fd_set 볡사가 ν•„μš” μ—†μœΌλ―€λ‘œ κ°€μž₯ λΉ λ₯Έ 속도λ₯Ό μ œκ³΅ν•œλ‹€

 

βž• μΈν„°λŸ½νŠΈμ™€ 트랩

μ‹œμŠ€ν…œμ—μ„œ λ°œμƒν•˜λŠ” 비동기적인 사건을 μ•Œλ¦¬λŠ” λ©”μ»€λ‹ˆμ¦˜

μΈν„°λŸ½νŠΈ : ν˜„μž¬ μˆ˜ν–‰ 쀑인 ν”„λ‘œμ„ΈμŠ€μ™€ 관계없이 ν•˜λ“œμ›¨μ–΄μ μΈ 사건을 μ•Œλ¦¬λŠ” λ©”μ»€λ‹ˆμ¦˜

트랩 : ν˜„μž¬ μˆ˜ν–‰ 쀑인 ν”„λ‘œκ·Έλž¨μ΄ μœ λ°œν•œ μ†Œν”„νŠΈμ›¨μ–΄μ μΈ μ‚¬κ±΄μ˜ λ°œμƒμ„ μ•Œλ¦¬λŠ” λ©”μ»€λ‹ˆμ¦˜

 

β˜‘οΈ select ν•¨μˆ˜

I/O λ©€ν‹° ν”Œλž™μ‹±μ„ μœ„ν•œ λŒ€ν‘œμ μΈ ν•¨μˆ˜κ°€ select

select() ν•¨μˆ˜λŠ” 파일 λ””μŠ€ν¬λ¦½ν„° λ³€ν™”λ₯Ό ν™•μΈν•˜λŠ”λ°, 파일 λ””μŠ€ν¬λ¦½ν„°λ₯Ό 톡해 데이터 μ†‘μˆ˜μ‹ μ΄ κ°€λŠ₯ν•œ μƒνƒœμΈμ§€ ν™•μΈν•˜λŠ” 것

λ³€ν™”λ₯Ό κ°μ§€ν•΄μ„œ ν•˜λ‚˜μ˜ μ „μ„±λ‘œλ₯Ό λ™μ‹œμ— μ—¬λŸ¬ μ‚¬μš©μžκ°€ μ‚¬μš©ν•  수 μžˆλ„λ‘ νš¨μœ¨μ„±μ„ κ·ΉλŒ€ν™”ν•  수 μžˆλ‹€

 

select() ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” 파일 λ””μŠ€ν¬λ¦½ν„°μ˜ λ²”μœ„λ₯Ό μ„€μ •ν•΄μ•Ό ν•œλ‹€

효율적인 μˆ˜ν–‰μ„ μœ„ν•΄ 확인해야 ν•˜λŠ” 파일 λ””μŠ€ν¬λ¦½ν„°μ˜ λ²”μœ„λ₯Ό μ œν•œν•˜λŠ” 것이닀

νƒ€μž„μ•„μ›ƒ μ‹œκ°„μ„ μ„€μ •ν•  수 μžˆλŠ”λ° select() ν•¨μˆ˜κ°€ ν˜ΈμΆœν•  λ•Œ 이벀트 λ°œμƒμ— λŒ€ν•΄ μ–Όλ§ˆλ§ŒνΌ 기닀릴 것인지λ₯Ό 지정할 수 μžˆλ‹€

int select (int nfds, fd_set *readfds, fd_set *writefds, fd_Set *exceptfds, struct timeval *timeout);

select() ν•¨μˆ˜μ˜ 첫 번째 인자 (nfds) λŠ” μƒνƒœλ₯Ό μ§€μΌœλ³Ό 파일 λ””μŠ€ν¬λ¦½ν„°μ˜ 제일 높은 번호

일반적으둜 파일 λ””μŠ€ν¬λ¦½ν„°μ˜ "κ°€μž₯ 큰 λ””μŠ€ν¬λ¦½ν„° 번호 + 1" 을 이용

μ‹€ν–‰ 쀑에 νŒŒμΌμ΄λ‚˜ μ†ŒμΌ“μ„ μ—΄μ—ˆλ‹€κ°€ λ‹«μœΌλ©΄ 파일 λ””μŠ€ν¬λ¦½ν„°λŠ” 쀑간 λ²ˆν˜Έκ°€ λΉ„κ²Œ λ˜λŠ”λ°, μ΄λ•Œ nfds 에 λ“€μ–΄κ°ˆ μˆ«μžλŠ” 전체 κ°œμˆ˜κ°€ μ•„λ‹Œ μ—΄λ¦° 파일 λ””μŠ€ν¬λ¦½ν„°μ˜ 제일 높은 λ²ˆν˜Έμ΄λ―€λ‘œ μ£Όμ˜ν•΄λΌ

 

λ§ˆμ§€λ§‰ 인자 (timeout) 은 ν•¨μˆ˜ 호좜 ν›„, λ¬΄ν•œ λŒ€κΈ° μƒνƒœμ— 빠지지 μ•Šλ„λ‘ νƒ€μž„μ•„μ›ƒμ„ μ„€μ •ν•  수 μžˆλ‹€

timeval ꡬ쑰체λ₯Ό μ‚¬μš©ν•˜λ©΄ λœλ‹€

인자 λ‚΄μš© λΉ„κ³ 
readfds μˆ˜μ‹ ν•  데이터가 μžˆλŠ”μ§€ 확인 μž…λ ₯ 버퍼에 데이터 쑴재
writefds 데이터 전솑이 κ°€λŠ₯ν•œ μƒνƒœμΈμ§€ 확인 좜λ ₯ 버퍼에 μΆ©λΆ„ν•œ μ—¬μœ  곡간 쑴재
exceptfds μ†ŒμΌ“μ΄ μ˜ˆμ™Έ 상황이 λ°œμƒν–ˆλŠ”μ§€ 확인 OOB λ©”μ‹œμ§€ 전솑 λ“±μ˜ 사항

ν•¨μˆ˜ ν˜ΈμΆœμ— μ„±κ³΅ν•˜λ©΄ 0 μ΄μƒμ˜ κ°’ λ°˜ν™˜, 0이 λ°˜ν™˜λ˜λ©΄ timeout 으둜 μ„€μ •λœ μ‹œκ°„μ— νƒ€μž„μ•„μ›ƒμ΄ λ˜μ—ˆλ‹€λŠ” 의미

였λ₯˜κ°€ λ°œμƒν•˜λ©΄ -1

 

select() ν•¨μˆ˜λŠ” fd_set μžλ£Œν˜• μ‚¬μš©ν•œλ‹€

보고 싢은 파일 λ””μŠ€ν¬λ¦½ν„°μ˜λ²ˆν˜Έλ₯Ό fd_set μžλ£Œν˜•μ— λ§ˆν¬ν•˜λŠ”λ°, 기본적으둜 μ‹œκ·Έλ„ 마슀크처럼 ν•˜λ‚˜μ˜ λΉ„νŠΈκ°€ ν•΄λ‹Ή 파일 λ””μŠ€ν¬λ¦½ν„° λ²ˆν˜Έμ™€ μΌμΉ˜ν•˜λŠ” ν˜•νƒœλ‘œ λ˜μ–΄ μžˆλ‹€

fd_set μžλ£Œν˜•μ„ μ„€μ •ν•œ ν›„ select() ν•¨μˆ˜μ— λ“±λ‘ν•΄μ„œ μ‹€ν–‰ν•˜λ©΄, μ΄λ²€νŠΈκ°€ λ°œμƒν•  λ•ŒκΉŒμ§€ select() ν•¨μˆ˜κ°€ λŒ€κΈ°ν•˜κ³  μžˆλ‹€κ°€, ν•΄λ‹Ή μ΄λ²€νŠΈκ°€ λ°œμƒν–ˆμ„ λ•Œ λ³€ν™”κ°€ 생긴 파일 λ””μŠ€ν¬λ¦½ν„°λ₯Ό 확인할 수 μžˆλ‹€

 

POSIX μ—μ„œ μ‚¬μš©ν•˜λŠ” timespec ꡬ쑰체λ₯Ό μ‚¬μš©ν•˜λŠ” pselect() ν•¨μˆ˜λ„ μžˆλ‹€

νƒ€μž„μ•„μ›ƒ μ‹œκ°„μ„ λ‚˜λ…Έμ΄ˆλ‘œ μ„€μ •ν•  수 있고, 파일 λ””μŠ€ν¬λ¦½ν„° 쑰사 쀑 μ‹œκ·Έλ„μ΄ λ°œμƒν•˜λŠ” 것을 블둝할 수 μžˆλ„λ‘ μ§€μ›ν•œλ‹€

// select_server.c
// TCP μ„œλ²„μ—μ„œ select() ν•¨μˆ˜λ₯Ό μ΄μš©ν•˜μ—¬ 병렬 μ²˜λ¦¬ν•˜κΈ°
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define SERVER_PORT 5100 		/* μ„œλ²„μ˜ 포트 번호 */

int main(int argc, char **argv)
{
    int ssock;
    socklen_t clen;
    int n;
    struct sockaddr_in servaddr, cliaddr;
    char mesg[BUFSIZ];
    fd_set readfd; 			/* select( ) ν•¨μˆ˜λ₯Ό μœ„ν•œ μžλ£Œν˜• */
    int maxfd, client_index, start_index;
    int client_fd[5] = {0}; 		/* ν΄λΌμ΄μ–ΈνŠΈμ˜ μ†ŒμΌ“ FD λ°°μ—΄ */

    /* μ„œλ²„ μ†ŒμΌ“ λ””μŠ€ν¬λ¦½ν„° μ—°λ‹€. */
    if((ssock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket()");
        return -1;
    }

    memset(&servaddr, 0, sizeof(servaddr)); 	/* μš΄μ˜μ²΄μ œμ— μ„œλΉ„μŠ€ 등둝 */
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERVER_PORT);

    if(bind(ssock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
        perror("bind()");
        return -1;
    }

    if(listen(ssock, 8) < 0) { 		/* ν΄λΌμ΄μ–ΈνŠΈμ˜ μ†ŒμΌ“λ“€μ„ μœ„ν•œ 큐 생성 */
        perror("listen()");
        return -1;
    }

    FD_ZERO(&readfd); 			/* fd_set μžλ£Œν˜•μ„ λͺ¨λ‘ 0으둜 μ΄ˆκΈ°ν™” */
    maxfd = ssock; 			/* ν˜„μž¬ μ΅œλŒ€ 파일 λ””μŠ€ν¬λ¦½ν„°μ˜ λ²ˆν˜ΈλŠ” μ„œλ²„ μ†ŒμΌ“μ˜ λ””μŠ€ν¬λ¦½ν„° */
    client_index = 0;

    do {
        FD_SET(ssock, &readfd); 	/* 읽기 λ™μž‘ 감지λ₯Ό μœ„ν•œ fd_set μžλ£Œν˜• μ„€μ • */

        /* ν΄λΌμ΄μ–ΈνŠΈμ˜ μ‹œμž‘ μ£Όμ†ŒλΆ€ν„° λ§ˆμ§€λ§‰ μ£Όμ†ŒκΉŒμ§€ fd_set μžλ£Œν˜•μ— μ„€μ • */
        for(start_index = 0; start_index < client_index; start_index++) {
            FD_SET(client_fd[start_index], &readfd);
            if(client_fd[start_index] > maxfd)
                maxfd = client_fd[start_index]; 	/* κ°€μž₯ 큰 μ†ŒμΌ“μ˜ 번호λ₯Ό μ €μž₯ */
        }
        maxfd = maxfd + 1;

        /* select( ) ν•¨μˆ˜μ—μ„œ 읽기가 κ°€λŠ₯ν•œ λΆ€λΆ„λ§Œ 쑰사 */
        select(maxfd, &readfd, NULL, NULL, NULL); 	/* 읽기가 κ°€λŠ₯ν•΄μ§ˆ λ•ŒκΉŒμ§€ λΈ”λ‘œν‚Ή */
        if(FD_ISSET(ssock, &readfd)) { 			/* 읽기가 κ°€λŠ₯ν•œ μ†ŒμΌ“μ΄ μ„œλ²„ μ†ŒμΌ“μΈ 경우 */
            clen = sizeof(struct sockaddr_in);		/* ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­ 받아듀이기 */
            int csock = accept(ssock, (struct sockaddr*)&cliaddr, &clen);
            if(csock < 0) {
                perror("accept()");
                return -1;
            } else {
                /* λ„€νŠΈμ›Œν¬ μ£Όμ†Œλ₯Ό λ¬Έμžμ—΄λ‘œ λ³€κ²½ */
                inet_ntop(AF_INET, &cliaddr.sin_addr, mesg, BUFSIZ);
                printf("Client is connected : %s\n", mesg);

                /* μƒˆλ‘œ μ ‘μ†ν•œ ν΄λΌμ΄μ–ΈνŠΈμ˜ μ†ŒμΌ“ 번호λ₯Ό fd_set에 μΆ”κ°€ */
                FD_SET(csock, &readfd);
                client_fd[client_index] = csock;
                client_index++;
                continue;
            }
            if (client_index == 5) break;
        }

        /* 읽기 κ°€λŠ₯ν–ˆλ˜ μ†ŒμΌ“μ΄ ν΄λΌμ΄μ–ΈνŠΈμ˜€λ˜ 경우 */
        for(start_index = 0; start_index < client_index; start_index++) {
            /* for 루프λ₯Ό μ΄μš©ν•΄μ„œ ν΄λΌμ΄μ–ΈνŠΈλ“€μ„ λͺ¨λ‘ 쑰사 */
            if(FD_ISSET(client_fd[start_index], &readfd)) {
                memset(mesg, 0, sizeof(mesg));

                /* ν•΄λ‹Ή ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ λ©”μ‹œμ§€λ₯Ό 읽고 λ‹€μ‹œ 전솑(Echo) */
                if((n = read(client_fd[start_index], mesg, sizeof(mesg))) > 0) {
                    printf("Received data : %s", mesg);
                    write(client_fd[start_index], mesg, n);
                    close(client_fd[start_index]); 	/* ν΄λΌμ΄μ–ΈνŠΈ μ†ŒμΌ“μ„ λ‹«λŠ”λ‹€. */

                    /* ν΄λΌμ΄μ–ΈνŠΈ μ†ŒμΌ“μ„ μ§€μš΄λ‹€. */
                    FD_CLR(client_fd[start_index], &readfd);
                    client_index--;
                }
            }
        }
    } while(strncmp(mesg, "q", 1));

    close(ssock); 					/* μ„œλ²„ μ†ŒμΌ“μ„ λ‹«λŠ”λ‹€. */

    return 0;
}

/*
ν΄λΌμ΄μ–ΈνŠΈμ™€μ˜ 톡신을 μœ„ν•œ μ†ŒμΌ“μ„ 생성 ν›„, μš΄μ˜μ²΄μ œμ— μ„œλΉ„μŠ€λ₯Ό λ“±λ‘ν•˜κ³ , ν΄λΌμ΄μ–ΈνŠΈμ˜ 접속을 μ²˜λ¦¬ν•  수 μžˆλŠ” λŒ€κΈ° 큐λ₯Ό μƒμ„±ν•˜λŠ” κΈ°λ³Έ μž‘μ—…μ„ μˆ˜ν–‰ν•¨
TCP μ„œλ²„μ™€ λ‹€λ₯Έ 뢀뢄은 select() ν•¨μˆ˜μ—μ„œ μ‚¬μš©ν•  fd_set λ³€μˆ˜ν˜•μ„ μ„ μ–Έν•œ λΆ€λΆ„μž„

select() ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•˜λ”λΌλ„ 일반적인 ν΄λΌμ΄μ–ΈνŠΈ 톡신 μ²˜λ¦¬λŠ” κΈ°λ³Έ TCP μ„œλ²„μ™€ λΉ„μŠ·ν•¨
select() ν•¨μˆ˜λ₯Ό μ΄μš©ν•΄μ„œ μ„œλ²„ μ†ŒμΌ“μ΄λ‚˜ ν΄λΌμ΄μ–ΈνŠΈ μ†ŒμΌ“μ—μ„œ 읽을 데이터가 μžˆλŠ”μ§€ ν™•μΈν•˜κ³  이λ₯Ό 각각 μ²˜λ¦¬ν•˜λŠ” 것

fd_set μžλ£Œν˜•μ„ FD_ZERO() 맀크둜λ₯Ό 톡해 λͺ¨λ“  λΉ„νŠΈλ₯Ό 0으둜 μ΄ˆκΈ°ν™”ν•˜κ³ , μ„œλ²„ μ†€μΌ“μ˜ 파일 λ””μŠ€ν¬λ¦½ν„°μ— λŒ€ν•œ 번호λ₯Ό FD_SET() 맀크둜λ₯Ό 톡해 μ„€μ •ν•œλ‹€
select() ν•¨μˆ˜λ₯Ό μ΄μš©ν•˜μ—¬ ν˜„μž¬μ˜ fd_set μžλ£Œν˜•μ— λŒ€ν•΄ 읽기가 κ°€λŠ₯ν•œ μ΄λ²€νŠΈκ°€ μžˆλŠ”μ§€ ν™•μΈν•˜λŠ”λ°, κ°€μž₯ λ§ˆμ§€λ§‰ 인자(timeout) κ°€ NULL 둜 μ„€μ •λ˜μ–΄ μžˆμœΌλ―€λ‘œ 읽기가 κ°€λŠ₯ν•  λ•ŒκΉŒμ§€ λ¬΄ν•œ λŒ€κΈ°

μ„œλ²„μ— γ…‘γ„ΉλΌμ΄μ–ΈνŠΈλ‘œλΆ€ν„°μ ‘μ†μ΄ λ“€μ–΄μ˜€λ©΄ select() ν•¨μˆ˜μ—μ„œ λΉ μ Έλ‚˜μ™€ fd_set 에 μ–΄λ–€ λΉ„νŠΈκ°€ μ„€μ •λ˜μ–΄ μžˆλŠ”μ§€ 확인 κ°€λŠ₯
FD_ISSET() 맀크둜λ₯Ό μ‚¬μš©ν•˜κ³ , λ§Œμ•½ μ„œλ²„ μ†ŒμΌ“μ—μ„œ μ΄λ²€νŠΈκ°€ λ°œμƒν–ˆμœΌλ©΄ accept() ν•¨μˆ˜λ₯Ό 톡해 ν΄λΌμ΄μ–ΈνŠΈμ˜ 접속을 받아듀이고, fd_set에 FD_SET() 맀크둜λ₯Ό 톡해 μƒˆλ‘œ μ ‘μ†ν•œ ν΄λΌμ΄μ–ΈνŠΈμ˜ μ†ŒμΌ“μ— λŒ€ν•œ 파일 λ””μŠ€ν¬λ¦½ν„°λ₯Ό μΆ”κ°€ν•œλ‹€

μ„œλ²„λŠ” λ‹€μ‹œ select() ν•¨μˆ˜λ‘œ λŒμ•„κ°€μ„œ μ„œλ²„λ‚˜ ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ 데이터가 μ˜€λŠ”μ§€ ν™•μΈν•˜κ³ , μƒˆλ‘œμš΄ ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­μ΄ λ“€μ–΄μ˜€λ©΄ 이전 μž‘μ—…μ„ λ‹€μ‹œ μˆ˜ν–‰ν•œλ‹€
이미 μ ‘μ†ν•œ ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ λ©”μ‹œμ§€κ°€ λ“€μ–΄μ˜€λŠ” 경우라면 λ‹€μŒ μž‘μ—…μ„ μˆ˜ν–‰ν•œλ‹€
for 루프와 FD_ISSET() 맀크둜λ₯Ό μ΄μš©ν•΄μ„œ 이전에 μ ‘μ†λœ ν΄λΌμ΄μ–ΈνŠΈλ“€μ˜ μ†ŒμΌ“μ˜ λ°°μ—΄μ—μ„œ μ–΄λ–€ ν΄λΌμ΄μ–ΈνŠΈλ‘œλΆ€ν„° 데이터가 λ“€μ–΄μ™”λŠ”μ§€ ν™•μΈν•˜κ³ , 데이터가 λ“€μ–΄μ™”μœΌλ©΄
read() ν•¨μˆ˜λ₯Ό 톡해 데이터λ₯Ό 읽은 ν›„ λ‹€μ‹œ write() ν•¨μˆ˜λ₯Ό 톡해 ν΄λΌμ΄μ–ΈνŠΈλ‘œ λ™μΌν•œ γ…λ„€μ‹œμ§€λ₯Ό 보낸닀

TCP ν΄λΌμ΄μ–ΈνŠΈλ₯Ό μ΄μš©ν•΄μ„œ μ„œλ²„μ— 접속할 수 μžˆλ‹€
*/
gcc -o select_server select_server.c 
./select_server &

 gcc -o tcp_client tcp_client.c
./tcp_client 127.0.0.1

 

β˜‘οΈ epoll API

epoll 은 λ¦¬λˆ…μŠ€ ν™˜κ²½μ—μ„œ μ‚¬μš©ν•  수 μžˆλ„λ‘ λ§Œλ“  I/O 톡지 κΈ°λ²•μœΌλ‘œ μž…λ ₯κ³Ό 좜λ ₯을 λ‹΄λ‹Ήν•  포트λ₯Ό μ§€μ •ν•˜μ—¬ μ²˜λ¦¬ν•˜κΈ° λ•Œλ¬Έμ— select 와 같이 전체 파일 λ””μŠ€ν¬λ¦½ν„°μ— λŒ€ν•΄ 순차적으둜 κ°μ‹œν•˜μ§€ μ•Šκ³ , 전체 파일 λ””μŠ€ν¬λ¦½ν„°λ„ λ³΅μ‚¬ν•˜μ§€ μ•ŠλŠ”λ‹€

 

μš΄μ˜μ²΄μ œκ°€ 직접 파일 λ””μŠ€ν¬λ¦½ν„°μ˜ 정보λ₯Ό 담은 μ €μž₯μ†Œλ₯Ό κ΄€λ¦¬ν•˜κ³  κ΄€μ°° λŒ€μƒμ„ μœ„ν•œ μ €μž₯μ†Œμ˜ 생성을 μš”μ²­ν•˜λ©΄, κ·Έ μ €μž₯μ†Œμ— ν•΄λ‹Ήν•˜λŠ” 파일 λ””μŠ€ν¬λ¦½ν„° (epoll_fd) λ₯Ό λ°˜ν™˜ν•œλ‹€

무언가 μ΄λ²€νŠΈκ°€ λ°œμƒν•˜λ©΄ epoll_fd λ₯Ό 톡해 확인할 수 μžˆλ‹€

epoll은 주둜 epoll_create(), epoll_ctl(), epoll_wait() ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•΄μ„œ 병행 μ„œλ²„λ₯Ό λ§Œλ“€ 수 μžˆλ‹€

// epoll_server.c
// TCP μ„œλ²„ μ½”λ“œλ₯Ό epoll 의 ν•¨μˆ˜λ₯Ό μ΄μš©ν•˜μ—¬ 병렬 μ²˜λ¦¬ν•  수 μžˆλ„λ‘ λ³€κ²½ν•˜κΈ°
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/epoll.h>

#define SERVER_PORT 5100 			/* μ„œλ²„μ˜ 포트 번호 */
#define MAX_EVENT 32

/* 파일 λ””μŠ€ν¬λ¦½ν„°λ₯Ό λ„ŒλΈ”λ‘œν‚Ή λͺ¨λ“œλ‘œ μ„€μ • */
void setnonblocking(int fd)
{
    int opts = fcntl(fd, F_GETFL);
    opts |= O_NONBLOCK;
    fcntl(fd, F_SETFL, opts);
}

int main(int argc, char **argv)
{
    int ssock, csock;
    socklen_t clen;
    int n, epfd, nfds = 1; 			/* κΈ°λ³Έ μ„œλ²„ μΆ”κ°€ */
    struct sockaddr_in servaddr, cliaddr;
    struct epoll_event ev;
    struct epoll_event events[MAX_EVENT];
    char mesg[BUFSIZ];

    /* μ„œλ²„ μ†ŒμΌ“ λ””μŠ€ν¬λ¦½ν„°λ₯Ό μ—°λ‹€. */
    if((ssock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        perror("socket()");
        return -1;
    }

    setnonblocking(ssock); 			/* μ„œλ²„λ₯Ό λ„ŒλΈ”λ‘œν‚Ή λͺ¨λ“œλ‘œ μ„€μ • */

    memset(&servaddr, 0, sizeof(servaddr)); 	/* μš΄μ˜μ²΄μ œμ— μ„œλΉ„μŠ€ 등둝 */
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(SERVER_PORT);
    if(bind(ssock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
        perror("bind()");
        return -1;
    }

    if(listen(ssock, 8) < 0) { 			/* ν΄λΌμ΄μ–ΈνŠΈμ˜ μ†ŒμΌ“λ“€μ„ μœ„ν•œ 큐 생성 */
        perror("listen()");
        return -1;
    }

    /* epoll_create() ν•¨μˆ˜λ₯Ό μ΄μš©ν•΄μ„œ 컀널에 등둝 */
    epfd = epoll_create(MAX_EVENT);
    if(epfd == -1) {
        perror("epoll_create()");
        return 1;
    }

    /* epoll_ctl() ν•¨μˆ˜λ₯Ό 톡해 κ°μ‹œν•˜κ³  싢은 μ„œλ²„ μ†ŒμΌ“μ„ 등둝 */
    ev.events = EPOLLIN;
    ev.data.fd = ssock;
    if(epoll_ctl(epfd, EPOLL_CTL_ADD, ssock, &ev) == -1) {
        perror("epoll_ctl()");
        return 1;
    }

    do {
        epoll_wait(epfd, events, MAX_EVENT, 500);
        for(int i = 0; i < nfds; i++) {
            if(events[i].data.fd == ssock) { 	/* 읽기가 κ°€λŠ₯ν•œ μ†ŒμΌ“μ΄ μ„œλ²„ μ†ŒμΌ“μΈ 경우 */
                clen = sizeof(struct sockaddr_in);

                /* ν΄λΌμ΄μ–ΈνŠΈμ˜ μš”μ²­ 받아듀이기 */
                csock = accept(ssock, (struct sockaddr*)&cliaddr, &clen);
                if(csock > 0) {
                    /* λ„€νŠΈμ›Œν¬ μ£Όμ†Œλ₯Ό λ¬Έμžμ—΄λ‘œ λ³€κ²½ */
                    inet_ntop(AF_INET, &cliaddr.sin_addr, mesg, BUFSIZ);
                    printf("Client is connected : %s\n", mesg);
                    setnonblocking(csock); 	/* ν΄λΌμ΄μ–ΈνŠΈλ₯Ό λ„ŒλΈ”λ‘œν‚Ή λͺ¨λ“œλ‘œ μ„€μ • */

                    /* μƒˆλ‘œ μ ‘μ†ν•œ ν΄λΌμ΄μ–ΈνŠΈμ˜ μ†ŒμΌ“ 번호λ₯Ό fd_set에 μΆ”κ°€ */
                    ev.events = EPOLLIN | EPOLLET;
                    ev.data.fd = csock;
                    epoll_ctl(epfd, EPOLL_CTL_ADD, csock, &ev);
                    nfds++;
                    continue;
                }
            } else if(events[i].events & EPOLLIN) { 	/* ν΄λΌμ΄μ–ΈνŠΈμ˜ μž…λ ₯ */
                if(events[i].data.fd < 0) continue; 	/* μ†ŒμΌ“μ΄ μ•„λ‹Œ 경우의 처리 */
                memset(mesg, 0, sizeof(mesg));

                /* ν•΄λ‹Ή ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ λ©”μ‹œμ§€λ₯Ό 읽고 λ‹€μ‹œ 전솑(Echo) */
                if((n = read(events[i].data.fd, mesg, sizeof(mesg))) > 0) {
                    printf("Received data : %s", mesg);
                    write(events[i].data.fd, mesg, n);
                    close(events[i].data.fd); 		/* ν΄λΌμ΄μ–ΈνŠΈ μ†ŒμΌ“μ„ λ‹«κ³  μ§€μš΄λ‹€. */
                    epoll_ctl(epfd, EPOLL_CTL_DEL, events[i].data.fd, NULL);
                    nfds--;
                }
            }
        }
    } while(strncmp(mesg, "q", 1));

    close(ssock); 				/* μ„œλ²„ μ†ŒμΌ“μ„ λ‹«λŠ”λ‹€. */

    return 0;
}

/*
TCP μ„œλ²„μ™€ 같이 μ†ŒμΌ“μ„ μƒμ„±ν•œ ν›„ bind()와 listen() ν•¨μˆ˜λ₯Ό μ΄μš©ν•΄μ„œ μ„œλΉ„μŠ€λ₯Ό μš΄μ˜μ²΄μ œμ— λ“±λ‘ν•œλ‹€
epoll_create() ν•¨μˆ˜λ₯Ό μ΄μš©ν•΄μ„œ κ°μ‹œν•˜κ³  싢은 파일 λ””μŠ€ν¬λ¦½ν„°μ˜ 숫자λ₯Ό λ„£μ–΄μ€¬λŠ”λ°, μ„œλ²„ + ν΄λΌμ΄μ–ΈνŠΈμ˜ 수둜 μ„€μ •ν•˜λ©΄ λœλ‹€
epoll_ctl() ν•¨μˆ˜μ— μ„œλ²„ μ†ŒμΌ“μ„ μΆ”κ°€ν•˜μ˜€λ‹€. μ„œλ²„ μ†ŒμΌ“μ€ ν΄λΌμ΄μ–ΈνŠΈμ˜ 접속을 κ°μ‹œν•  κ²ƒμ΄λ―€λ‘œ μž…λ ₯ (EPOLLIN) 으둜 이벀트 μ„€μ •

μ„œλ²„μ— ν΄λΌμ΄μ–ΈνŠΈκ°€ μ ‘μ†λ˜λ©΄ epoll_wait() ν•¨μˆ˜μ—μ„œ λ„˜μ–΄κ°€κ²Œ λ˜λŠ”λ°, μ΄λ²€νŠΈκ°€ λ°œμƒν•œ μ†ŒμΌ“μ΄ μ„œλ²„μ΄λ©΄ accpet() ν•¨μˆ˜λ₯Ό ν†΅ν•΄μ„œ 받아듀이고,
epoll_ctl() ν•¨μˆ˜λ₯Ό 톡해 λ‹€μ‹œ 컀널에 ν΄λΌμ΄μ–ΈνŠΈμ˜ μ†ŒμΌ“ λ””μŠ€ν¬λ¦½ν„°λ₯Ό κ°μ‹œν•  수 μžˆλ„λ‘ μΆ”κ°€ν•œλ‹€

ν΄λΌμ΄μ–ΈνŠΈ μ†ŒμΌ“μœΌλ‘œλΆ€ν„° μ΄λ²€νŠΈκ°€ λ°œμƒν•˜λ©΄ ν΄λΌμ΄μ–ΈνŠΈμ—μ„œ 온 데이터λ₯Ό μ½μ–΄μ„œ λ‹€μ‹œ ν΄λΌμ΄μ–ΈνŠΈλ‘œ 전솑(에코) ν•˜κ³  ν΄λΌμ΄μ–ΈνŠΈν•œν…Œ μ†ŒμΌ“μ„ 닫은 ν›„
epoll_ctl() ν•¨μˆ˜λ₯Ό 톡해 컀널에 μΆ”κ°€ν•œ ν΄λΌμ΄μ–ΈνŠΈ μ†ŒμΌ“μ˜ κ°μ‹œλ₯Ό μ‚­μ œν•œλ‹€

ν΄λΌμ΄μ–ΈνŠΈλŠ” tcl_client 둜 μ‚¬μš©
epoll 이 select 에 λΉ„ν•΄ μ†λ„λŠ” λΉ λ₯΄μ§€λ§Œ, νŠΉμ • ν”Œλž«νΌμ—μ„œλŠ” μ‚¬μš©ν•  수 μ—†λ‹€

gani@gani:~/raspi/NetworkProgramming $ gcc -o epoll_server epoll_server.c 
gani@gani:~/raspi/NetworkProgramming $ ./epoll_server &
gani@gani:~/raspi/NetworkProgramming $ gcc -o tcp_client tcp_client.c 
gani@gani:~/raspi/NetworkProgramming $ ./tcp_client 
gani@gani:~/raspi/NetworkProgramming $ ./tcp_client 127.0.0.1
*/
gcc -o epoll_server epoll_server.c 
./epoll_server &
gcc -o tcp_client tcp_client.c 
./tcp_client 
./tcp_client 127.0.0.1

 

 

 

 

728x90
λ°˜μ‘ν˜•
Comments