serial 버퍼를 초기화 하는 방법이요.

은영신랑의 이미지

두 대의 PC를 가지고 한쪽에서는 계속해서 write를 하고, 나머지 하나는
read를 해서 그 데이터를 처리합니다. 물론 serial port를 이용하고요.

그런데 주기가 200msec 정도가 되다 보니깐 (데이터수는 5개)
받는 쪽에서 바로바로 처리를 하는것이 아니고 전에 받았던것을 처리합니다.
만약 1000번을 보내면 보내는 쪽에서는 끝났는데 받는쪽에서는
계속해서 처리를 하다보니깐 시간 차이가 좀 납니다.

바로바로 처리를 하고, 시간적으로 차이가 없게 하려면 어떤 해결책을
써야 할까요?
시리얼의 read 버퍼를 clear, read하기전에 serial init등을 생각해보고 있는데 답변좀 부탁드립니다.

crimsoncream의 이미지

받는 쪽에서 polling을 하고 계실때, 수신한 data가 최신 data가 아니라서 생기는 문제이거나 polling을 해서 두 프로그램의 수행시간이 sync가 안되는 문제에 대한 질문이신 것 같은데. 맞다면 polling을 하시지 마시고 select를 이용하시거나 signal(SIGIO)를 이용해서 data가 도착할 때마다 바로바로 처리하거나 버퍼에 쌓아두고 나중에 처리하시도록 하시면 될 것 같은데요.

오늘 우리는 동지를 땅에 묻었습니다. 그러나 땅은 이제 우리들의 것입니다.
아직도 우리의 적은 강합니다. 그러나 우리는 그들보다 많습니다.
항상 많을 것입니다.

은영신랑의 이미지

저도 select를 사용하였구요, 대략적으로 아래와 같이 구현이 되어 있습니다. 또한 serial_read()함수는 자체의 스케줄링 방식에 의해서 100msec
마다 실행이 되도록 하였습니다.

void serial_read()
{
memset(readBuffer, 0, sizeof(readBuffer));

timeout.tv_sec = 0;
timeout.tv_usec = 8000000;
	
FD_ZERO(&readfdset);
FD_SET(fd, &readfdset);
	
select_result=select(FD_SETSIZE, &readfdset, NULL, NULL, &timeout);

switch(select_result){
		
      ....
     Error가 아니면 read하여 처리부분
 }

}
mach의 이미지

은영신랑 wrote:
저도 select를 사용하였구요, 대략적으로 아래와 같이 구현이 되어 있습니다. 또한 serial_read()함수는 자체의 스케줄링 방식에 의해서 100msec
마다 실행이 되도록 하였습니다.

void serial_read()
{
memset(readBuffer, 0, sizeof(readBuffer));

timeout.tv_sec = 0;
timeout.tv_usec = 8000000;
	
FD_ZERO(&readfdset);
FD_SET(fd, &readfdset);
	
select_result=select(FD_SETSIZE, &readfdset, NULL, NULL, &timeout);

switch(select_result){
		
      ....
     Error가 아니면 read하여 처리부분
 }

}

*참고
일단 님의 코드에서
timeout.tv_usec = 8000000;
는 잘못된 코드입니다.(오타?)
tv_usec구조체 멤버 값의 범위는 0-999,999까지 입니다. micro second니까 당연합니다.
8,000,000값을 넣으시면 0으로 잘라버릴것입니다. 따라서, select의 수행이 타임아웃없이
매번 폴링하는 형태가 되겠네요.

답변:
시리얼포트로부터 읽어서 처리하는 부분의 코드가 시간을 얼마나 잡아먹는지를
프로파일링해봐야 할듯합니다. 시간소모가 크다면 다중쓰레드나 다중프로세스등으로 처리를
좀 바꿔보는것도 보탬이 될듯도 한데요. 같이 고민해보실려면 해당 처리코드의 처리시간을
한번 재서 올려보심이 어떨는지요? 단지, switch()내에서 모든 처리를 수행한다면 말입니다.

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

익명 사용자의 이미지

저는 좀 다르게 처리합니다.

일단 받을거 다 받고 처리하는데 있어서

아직 버퍼에 뭔가 남아서 문제라면

받을거 다 받은후에 다시한번 버퍼를 비우도록 의미없는 읽기 시도를

하도록 처리합니다.

물론 가장 좋은 방법은 보내는 쪽에서 정확히 보내는것이 좋지만

받는쪽에서는 어느정도 버퍼를 소진시켜주는 것은 Serial 통신상에서는

필요하다고 생각합니다.

버퍼를 소진하는 방법은 여러가지가 있겠지만 몇가지를 들자면

1. fsync를 사용하는 방법.
2. open시에 O_SYNC를 인자로 주거나 NDELAY를 주는것.
3. FIONREAD를 이용하여 버퍼에 아직 소진되지 않는 데이터의 양을
점검후에 그만큼 읽어서 버리도록 처리
4. Serial device를 닫었다가 다시 오픈하는 방법.
5. lseek를 사용하여 마지막 포인터로 바꿔버리는 방법 (잘 안되는 경우가 많음.)

저의 경우 이런 방법을 쓰는데
주로 가장 선호하는 것은 3번 방법입니다.
그리고 마음 편하게 하려면 4번 방법도 좋고요.

특정주기를 가지고 하는 것도 좋을수 있지만
그럴바에는 select가 최선인듯 하고요...

crimsoncream의 이미지

select만 써가지고는 해결이 안되시면 위에 다른분이 쓰신 것처럼 thread나 fork를 이용하시는 거나 좀 변칙적이지만 SIGIO handler를 작성하셔서 그 안에서 read 작업을 수행해서 버퍼링 하도록 하시면 될 것 같은데요.

뭐 이딴 식으로요.

void
sigio_action (int signum..............)
{
  signal (SIGIO, SIG_IGN);
  select (..........);
  read (...........);
  signal (SIGIO, sigio_action);
}

좀 꼴 사나운 코드이긴 해도 동작은 그런데로 합니다.

오늘 우리는 동지를 땅에 묻었습니다. 그러나 땅은 이제 우리들의 것입니다.
아직도 우리의 적은 강합니다. 그러나 우리는 그들보다 많습니다.
항상 많을 것입니다.

mach의 이미지

minzkn wrote:

5. lseek를 사용하여 마지막 포인터로 바꿔버리는 방법 (잘 안되는 경우가 많음.)


character device중 하나인 시리얼 디바이스는 lseek을 사용할 수 없습니다.

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

익명 사용자의 이미지

mach wrote:
minzkn wrote:

5. lseek를 사용하여 마지막 포인터로 바꿔버리는 방법 (잘 안되는 경우가 많음.)


character device중 하나인 시리얼 디바이스는 lseek을 사용할 수 없습니다.

음... Character device라고 lseek가 안되는 것은 분명히 아닙니다.

그리고 Serial이 제가 말했듯이 잘 안되는 경우가 많으나
일부 다른 Serial device는 됩니다. 즉, 진짜 Serial device는 안되지만
(사실 구현만 제대로 하면 가능하다는 것은 아실겁니다.)
Device driver개발자가 신경좀 쓰면 됩니다. 일부 되는것이 있습니다.

하지만 대부분 지원하지 않는것이 사실입니다.

반드시 Character device라는 이유로 seek가 안된다는 것은
사실이 아닙니다.

수정: 분명 lseek(handle, 0, SEEK_END) 이렇게 하면
Serial에서 다음번 lseek(handle, 0, SEEK_CUR) 하면
버퍼가 비워져 있는 상태로 구현되면 됩니다.

mach의 이미지

minzkn wrote:
mach wrote:
minzkn wrote:

5. lseek를 사용하여 마지막 포인터로 바꿔버리는 방법 (잘 안되는 경우가 많음.)


character device중 하나인 시리얼 디바이스는 lseek을 사용할 수 없습니다.

음... Character device라고 lseek가 안되는 것은 분명히 아닙니다.

그리고 Serial이 제가 말했듯이 잘 안되는 경우가 많으나
일부 다른 Serial device는 됩니다. 즉, 진짜 Serial device는 안되지만
(사실 구현만 제대로 하면 가능하다는 것은 아실겁니다.)
Device driver개발자가 신경좀 쓰면 됩니다. 일부 되는것이 있습니다.

하지만 대부분 지원하지 않는것이 사실입니다.

반드시 Character device라는 이유로 seek가 안된다는 것은
사실이 아닙니다.

수정: 분명 lseek(handle, 0, SEEK_END) 이렇게 하면
Serial에서 다음번 lseek(handle, 0, SEEK_CUR) 하면
버퍼가 비워져 있는 상태로 구현되면 됩니다.


minzkn님을 포함하는 커널디바이스 드라이버를 직접 손댈수 있는 분들(전문가)에게는
옳은 얘기로 들려지겠지만, 커널디바이스 드라이버를 손대기 어려운/싫은 사람(비전문가 또는 무관심자)에게는
보다 일반적인 사실로 공고함이 옳은것 같습니다.
이해 대상에 따라 차이는 있으나, 일반적인 학습자를 배려하시는 모습도 보여주심이 좋을듯합니다.
* 참고로, 저도 minzkn님의 코드를 애독하는 사람임을 밝혀드립니다요. :lol:

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

익명 사용자의 이미지

예.

mach의 이미지

minzkn wrote:
예.

단지 저 뿐만이 아니라, 많은 분들이 멋진 minzkn을 상상할것으로 보입니다요.
:lol:

------------------ P.S. --------------
지식은 오픈해서 검증받아야 산지식이된다고 동네 아저씨가 그러더라.

지리즈의 이미지

많은 종류의 block device가 lseek를 지원하더군요...
전의 lseek가 원래 지원되는줄 알았습니다. :-)

There is no spoon. Neo from the Matrix 1999.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.