[회고] 신입 iOS 개발자가 되기까지 feat. 카카오 자세히보기

💻 CS/시스템프로그래밍

[시스템프로그래밍] UNIX I/O - 1

inu 2019. 11. 4. 22:50
반응형

 

이것은 한낱 대학생이 교수의 수업을 듣고 작성한 개인저장용 복습 문서입니다.

 

그렇지만, 물론 지적과 수정은 환영합니다.


• Peripheral device (주변기기)
– 컴퓨터시스템에 의해 접근 가능한 하드웨어의 일종 
– ex) disks, tapes, CD-ROMs, screens, keyboards, printers, mouse devices and network interfaces


• Device driver
– 'device driver'라 불리는 시스템모듈에 의해 실행되는 시스템콜 함수가 각 입출력 장치를 제어할 수 있게 해준다.

– 'device driver'는 용인되지 않은 사용을 막고, 디바이스의 실행의 디테일적인 부분을 숨긴다.

입출력은 어디에나 필요한 common한 task이다. 따라서 좀 더 간편할 필요성이 있다.

그래서 생긴것이 I/O 함수들. (open,close,read,write and octl)

이 함수들은 디바이스의 종류와 상관없이 모두 파일로 처리한다.

(called special files, /dev에 위치.)


유닉스 파일은 byte단위의 개체가 모여있는 것 (B0, B1, .... , Bk , .... , Bm-1)

I/O도 바이트 단위로 진행된다.

– /dev/sda2 (hard disk partition)

– /dev/tty2 (terminal)

심지어 OS 커널 이미지로 파일로 표현된다.

– /dev/kmem (kernel memory image)

– /proc (kernel data structures)


기본적인 Unix I/O operations (system calls)


– Opening and closing files
• open()- 사용자가 I/O 디바이스에 접근 의사를 밝히는 것.
• close()- 접근을 종료하는 것.


– 현재 파일의 포지션 변경 (seek)
• 커널이 file position을 변경한다. 처음엔 모든 파일이 0이다.
• seek()- 명시적으로 file position을 변경하는 것.


– Reading and writing a file
• read()- 파일로부터 메모리에 n 바이트만큼 읽는다.

• write()- 메모리로부터 파일에 n바이트만큼 쓴다.

 


Reading

 

#include 
ssize_t read(int fildes, void * buf, size_t nbyte); 

 

-fildes : 파일디스크립터, 오픈된 파일을 가르키는 값

-buf : 파일이 써질 메모리의 위치. nbyte보다 커야한다.

-nbyte : 몇 바이트를 읽을지. buf보다 커선 안된다.

-ssize_t : signed integer data type의 일종. 읽은 바이트 수

-size_t : unsigned integer data type.

 

-실제로 읽은 바이트 수 리턴

-실패시 -1 리턴

-nbyte만큼 읽기 전에 파일이 끝나버리면 nbyte보다 더 적게 리턴될 수도 있다.


File descriptor

 

오픈된 파일을 가르키는 지시자

오픈함수의 리턴값

단, 예외 3가지는 오픈함수 없이도 File descriptor를 읽어올 수 있다.

 

• STDIN_FILENO (표준 입력장치)
– 키보드입력에 대응 
– In legacy code : 0.
• STDOUT_FILENO (표준 출력 장치)
– 스크린출력에 대응
– In legacy code : 1.
• STDERR_FILENO (표준 에러 장치)
– 프로그램이 절대 이것을 종료해선 안됨
– In legacy code : 2. 


표준 입력장치로부터 buf에 최대 100바이트의 code segment를 입력하게 하려고 한다면?

 

char buf[100];
ssize_t bytesread;
bytesread = read (STDIN_FILENO, buf, 100);

 

-> code segment가 할당을 제대로 안해서 결과가 이상하게 나올 수 있다.

 


Readline.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <errno.h>
#include <unistd.h>
int readline(int fd, char *buf, int nbyte) {
    int numread = 0;
    int returnval;
    while (numread < nbytes -1) {
        returnval = read (fd, buf + numread, 1);
        if ((returnval == -1&& (errorno == EINTR))
            continue;
        if ((returnval == 0&& (numread == 0)
            return 0;
        if (returnval == 0)
            break;
        if (returnval == -1)
            return -1;
       numread++;
        If (buf[numread-1== ‘\n’) {
            buf[numread] = ‘\0;
            return numread;
        }
    }
    errno = EINVAL;
    return -1;
}
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
 

-numred : 읽은 바이트 수 저장용

-하나의 라인을 바이트 하나하나씩 읽어주는 함수

-buf + numred에 쓰기 시작하므로 이어쓰기 가능 (buf+0, buf+1,....)

-개행문자 ('\n')을 만나면 끝난 것으로 인식하고 해당자리에 '\0'을 넣어서 하나의 string으로 처리해놓는다.

-numred가 nbyte만큼 읽기 전에 무언가로 리턴이 되지 않고 반복문을 빠져나오면 오류 (EINVAL : 한줄이 아니다.)

-EINTR인 에러가 발생하면 다시 리드

-0만큼 읽혔는데 numred도 0이면 0리턴

-0만큼 읽혔는데 numred가 0이 아니면 오류 -> break -> EINVAL 오류리턴

-EINTR가 아닌 에러가 발생하면 -1 (그냥 오류, EINVAL은 아님) 리턴

 

When does an error occur?
1. read()에서 에러발생가능
2. 하나 이상의 파일을 읽고나면 새 라인을 읽기 전에 파일 종료가 될 수 있음.
3. nbytes-1 바이트만큼 읽고 더 이상 새로운 라인이 발견되지 않는다.

 

-> 아래와 같은 코드는 표준입력장치로부터 최대 99바이트를 한줄 읽어온다.

int bytesread;
char mybuf[100];
bytesread = readline(STDIN_FILENO, mybuf, sizeof(mybuf));


Writing

 

#include 
ssize_t write(int fildes, const void *buf, size_t nbyte); 

 

-기본적 구조는 read()와 유사

-실질적으로 쓴 바이트값 리턴

-0보다 크고 nbyte보다 작은 값이라면 오류가 나지 않은것


File offset

 

다음에 수행할 I/O operation의 위치를 가르키는 값

read/write는 file offset 자리에서부터 시작되고, 

read/write가 실행될때마다 자동적으로 file offset이 변경된다.

 

fd = open(“test.dat”, O_RDWR);
read(fd, buf, 100);
write(fd, buf,200);
read(fd, buf, 200); //이와 같은 상황에서는 이 read는 100만 리턴한다. (write할 공간부족)
close(fd);


echo1

 

#define BLKSIZE 1024
char buf[BLKSIZE];
read(STDIN_FILENO, buf, BLKSIZE); // 키보드로부터, buf에 BLKSIZE 만큼 읽는다.
write(STDOUT_FILENO, buf, BLKSIZE); // buf에 있는 것을 스크린에 BLKSIZE 만큼 쓴다.

 

문제점 : read() 작동시 읽어오는 것을 실패하거나 혹은 BLKSIZE 바이트만큼 불러오지 않을 수 있다.

그러면 write() 작동시 그만큼 garbage값을 출력한다.

 

echo2

 

#define BLKSIZE 1024
char buf[BLKSIZE];
ssize_t bytesread;
bytesread = read(STDIN_FILENO, buf, BLKSIZE);
if (bytesread >0)
    write(STDOUT_FILENO, buf, bytesread);

 

개선 : write() 사용시 0보다 클때, read()의 리턴값인 bytesread만큼만 쓴다.

 

문제점 : write()의 특성상 bytesread만큼 못하는 경우가 있을 수 있다. (함수의 특성)

read()나 write()나 둘 다 signal에 의해 인터럽트될 위험이 있다. (-1 리턴시 에러일수도 있지만 시그널일수도 있다.)

 

echo3

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
#include <errno.h>
#include <unistd.h>
#define BLKSIZE 1024
int copyfile(int fromfd, int tofd) { // fromfd : 소스, tofd : 타겟(빈파일)
    char *bp;
    char buf[BLKSIZE];
    int bytesread;
    int byteswritten = 0;
    int totalbytes = 0;
    for ( ; ; ) {
        while (((bytesread = read(fromfd, buf, BLKSIZE)) == -1&& (errno == EINTR)) ; /* handle interruption by signal */
        if (bytesread <= 0/* real error or end-of-file on fromfd */
            break;
        bp = buf; // bp : 얼마나 썼나 확인하면서 이동 vs buf : 진짜 주소값
        while (bytesread > 0) {
            while(((byteswritten =write(tofd, bp, bytesread)) == -1 ) &&(errno == EINTR)) ;/* handle interruption by signal */
            if (byteswritten <= 0/* real error on tofd */
                break;
            totalbytes += byteswritten; 
            bytesread -= byteswritten; // 쓴만큼 빼주면서 0이 될때까지 계속 재입력
            bp += byteswritten; // 쓴만큼 bp에 더하기 (bp위치 이동)
        }
        if (byteswritten == -1/* real error on tofd */
            break;
    }
    return totalbytes;
}
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
s

개선 : signal 일경우 반복문 돌려서 재진행.

bp 업데이트 해가면서 반복적으로 write해서 조금 write되는 일이 없도록 방지

byteread가 0이 될때까지 무한 루프

 

bytesread의 역할 : 이번 스탭 타겟파일에 써야하는 byte수이면서 read의 리턴값


r_read(), r_write()
– 시그널에 의한 인터럽트 시 계속 다시 읽기/쓰기

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
ssize_t r_read(int fd, void *buf, size_t size) {
   ssize_t retval;
 
   while (retval = read(fd, buf, size), retval == -1 && errno == EINTR) ;
   return retval;
 
ssize_t r_write(int fd, void *buf, size_t size) {
   char *bufp;
   size_t bytestowrite;
   ssize_t byteswritten;
   size_t totalbytes;
 
   for (bufp = buf, bytestowrite = size, totalbytes = 0;
        bytestowrite > 0;
        bufp += byteswritten, bytestowrite -= byteswritten) {
      byteswritten = write(fd, bufp, bytestowrite);
      if ((byteswritten == -1&& (errno != EINTR))
         return -1;
      if (byteswritten == -1)
         byteswritten = 0;
      totalbytes += byteswritten;
   }
   return totalbytes;
}
 
 

• readwrite() 

– 어떠한 파일로부터 byte를 받고 다른 파일에 그것을 써준다.(using r_read() and r_write()) 

– 어떤 buffer의 사이즈가 PIPE_BUF이면, 이는 파이프에 writing할때 유용하다.
1
2
3
4
5
6
7
8
9
10
11
12
13
int readwrite(int fromfd, int tofd) {
   char buf[BLKSIZE];
   int bytesread;
 
   if ((bytesread = r_read(fromfd, buf, BLKSIZE)) == -1)
      return -1;
   if (bytesread == 0)
      return 0;
   if (r_write(tofd, buf, bytesread) == -1)
      return -1;
   return bytesread;
}
 
 

왜냐하면 파이프에 PIPE_BUF(혹은 그보다 조금)만큼 쓰는 것은 atomic하기 때문이다. 

즉, 쪼개지는 오류가 없다.


• copyfile()
– readwrite()를 이용해 파일을 카피

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int copyfile(int fromfd, int tofd) {
   char buf[BLKSIZE]; 
   int bytesread, byteswritten;
   int totalbytes = 0;
 
   for (  ;  ;  ) {
      if ((bytesread = r_read(fromfd, buf, BLKSIZE)) <= 0)
         break;     
      if ((byteswritten = r_write(tofd, buf, bytesread)) == -1)
         break;
      totalbytes += byteswritten;
   }
   return totalbytes;
}
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
 

readblock()

 

100바이트만큼 읽겠다고 요청하면 100바이트만큼 읽을 때까지 무조건 반복하는 함수

인터럽트되거나 더 적게 읽었을때 계속 반복

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
ssize_t readblock(int fd, void *buf, size_t size) {
   char *bufp;
   size_t bytestoread;
   ssize_t bytesread;
   size_t totalbytes;
 
   for (bufp = buf, bytestoread = size, totalbytes = 0;
        bytestoread > 0;
        bufp += bytesread, bytestoread -= bytesread) {
      bytesread = read(fd, bufp, bytestoread);
      if ((bytesread == 0&& (totalbytes == 0))
         return 0;
      if (bytesread == 0) { 
         errno = EINVAL; 
         return -1;    
      } 
      if ((bytesread) == -1 && (errno != EINTR))
         return -1;
      if (bytesread == -1)
         bytesread = 0;
      totalbytes += bytesread;
   }
   return totalbytes;
}
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
 

요청 바이트 수만큼 읽지 못하면 끝나면 0 리턴

요청 바이트 수만큼 읽으면 해당 바이트수 리턴

에러시 -1 리턴


 

반응형