Unix Special File I/O

□Special files:

- Block devices: disk, tape 등.

Character devices: CRT, printer, modem 등.

- file I/O와 동일한 API를 사용. (open(), read(), write(), close() 등)

□Major and minor device numbers

- Every I/O device has major d.n. and minor d.n. in i_node table.

- In <sys/types.h>

dev_t	; short integer

8 bits	8 bits

Major d.n.  Minor d.n.	어디에 I/O를 할 것인가? (device 번호)

어떻게 I/O를 할 것인가? (device driver)

□Device I/O 자료구조

□Device I/O 절차

① system call인 read()혹은 write()는 정상적인 file 접근 방식으로 특수 file의 i_node에 접근이 가능하다.

② system은 장치가 block device인지 character device인지 알기 위해 i_node의 flag를 검사하여 major d.n.를 추출한다.

③ device table의 해당항목을 찾기 위해 major d.n.를 사용하고, 자료 전송을 위해 device-specific driver routine을 호출한다.

④ minor device number를 사용하여 해당 device에 I/O를 수행한다.

(minor d.n.: 1 개 이상의 device 출입구를 제공하는 장치들에 대해 어떤 장치가 접근되고 있는가를 식별.)

ex) terminals, disk drives 등의 경우.

□Create a device file

#mknod  /dev/tty13   c   2   13

char. sp. major  minor

□ Directories

- A special type of file that kernel maintains.

∀ A user process have to use system calls.

- A process can create a new directory with mkdir().

int mkdir(char * pathname, int mode);

the lower order 12 bits of st_mode	ex) 7777, 0644 등.

□Current working directory

- Each process has associated with it a c.w.d. in user block.

- A process can change its c.w.d. with chdir().

int chdir(char *pathname);

return 0 if ok.

-1 otherwise

* int system(char *string)  /* a process invokes a Unix command. */

I/O system calls

□Two methods for I/O

① The system calls for I/O provided by Unix kernel.

ex) open(), read(), write(), close(), lseek()

② The standard C library provided by C compiler.

ex) fopen(), fscanf(), fprintf(), fclose() 등.

□open()

- A file is opened by

#include <fcntl.h>

int open (  char *pathname,    int oflag    [,int mode]);

return f.d. if O.K.  absolute/  요청하는   the low-order 12 bits of

-1 otherwise       relative	접근모드   st_mode ∀	O_CREAT의 경우	만 사용 ex) 0(8진수)7777, 0666

- open()이 read()와 write()를 하기 위해 file을 개방하려고 하면, kernel은 요청된 접근 모드가 file access mode에 나타나 있는지를 조사.

∀ 만일 허용되지 않으면 return -1.

oflag에서 사용가능한 argument와 그 의미. (p. 39) - OR(|)가 가능함.

oflag의 사용가능한 argument	의미
O_RDONLY	읽기 전용으로 열기
O_WRONLY	쓰기 전용으로 열기
O_RDWR	읽기/쓰기로 열기
O_NDELAY	Do not block on open/read/write (p.111 fig.3-9 참고)
O_APPEND	쓰기의 경우 파일의 끝에 추가하기
O_CREAT	만약 파일이 존재하지 않을 경우 생성하여 열기.
O_TRUNC	만약 파일이 존재하면 그 파일의 길이를 0으로 하기 (내용이 모두 지워짐)
O_EXCL	O_CREAT이면서 파일이 존재하면 Error를 발생시킴.

ex) fd = open(pathname, O_WRONLY | O_CREAT | O_TRUNC, 0600);

□ file permission이 호출 process에 쓰기가 허용되어 있으면

fd = creat(pathname, 0600)와 동일.

fd = open(pathname, O_WRONLY | O_CREAT | O_EXCL, 0600);

□ 어떤 허가가 지정되더라도, file이 존재하면 open()은 실패.

O_NDELAY : FIFO(pipe)에서 생산자 프로세스가 데이터를 쓰지 않은 상태에서 소비자 프로세스가 데이터를 요구하거나, 생산자 프로세스가 데이터를 다 생성했는데도 소비자 프로세스가 읽지 않을 경우에 기다리지 않고(do not block) 즉시 return함.

ex)  > ps | more

□creat()

- A new file is created by

int creat(char *pathname, int mode);

return f.d. if O.K.

-1 otherwise

- A new file is opened for "writing".

- If the file is "already exists", its length is truncated to zero.

∀ 즉, creat()는 file이 이미 존재하는 경우에는 쓰기 허용이 되어 있어야만 앞에서부터 새로이 씀.

ex) #include <stdio.h>

#define PERM 0444	/* read only - owner included */

main()

{

int fd;

if ((fd = creat("test", PERM) < 0) {	//....①

fprintf(stderr, "1st creat failed\n");

exit(1);

}

close(fd);	/* leave the file empty  */

if ((fd = creat("test", PERM) < 0) {	//...②

fprintf(stderr, "2nd creat failed\n");

exit(1);

}

printf("This should not appear unless you are root\n");

printf("or the file already exists and allows writing.\n");

exit(0);

}

결과: test 파일 생성.

2nd creat failed.

∀ 위 program에서 test가 존재하지 않을 때, 첫 번째 creat() 호출로 file이 생성되며, 두 번째 creat() 호출에서는 소유자가 file에 쓰기를 허가하지 않았기 때문에 -1을 return한다. 그러나 사용자가 superuser이면 두 creat()는 성공하여 소유자가 file에 쓰는 것을 허용하지 않았더라도 test에 쓸 수 있다.

ex)

□ close()

- An open file is closed by

int close(int fd);

- When a process terminates, all open files are automatically "closed" by the kernel.

□read()

- Data is read from an open file using

int read(int fd, char *buff, unsigned int nbytes);

↓                   저장할 buffer      입력하려는 byte수(max: 65534+2)

return the actual no. of bytes read.	0: an end-of-file

0 : if the end of file in encountered.	-1: error

-1: otherwise

□write()

- Data is written to an open file using

int write(int fd, char *buff, unsigned int nbytes);

↓                     출력될 buffer      출력하려는 byte수(max: 65534)

return the actual no. of bytes written.

-1: otherwise

□lseek()

- Unix file: a long byte stream.

- current byte position : the no. of bytes from the start of the file.

- offset is 0 when open()/creat().

- Before read() or write(), an open file can be positioned using

long lseek(int fd, long offset, int whence);

↓         positive/negative   □ 어디서부터.

return the new offset if O.K.	0: from the beginning of the file

-1 otherwise	1: current + offset

2: size of the file + offset

- After read() or write(), offset is updated.

ex) residents file

resident #1		resident #2		resident #3
kim	\n	Lee	\n	Park C.H.	\0
← 40 char.s →		← 40 char.s →

offset:

roomno	offset
1	0
2	41
3	82
:	:

/* listoccupier.c - list all occupant's names */

#define NROOMS 10

#define NULLPTR (char *)0   /* '\0' */

main()

{

int i;

char *getoccupier(), *p;

for (i=1; i<=NROOMS; i++){

if ((p=getoccupier(i)!=NULLPTR)

printf("Room %d: %s\n", i, p);

else

printf("error on room %d\n", i);

}

}

#include <fcntl.h>

#define  NULLPTR  (char *) 0

#define  NAMELEN 41

char	namebuf[NAMELEN];	/* a buffer to hold a name */

int	inf = -1;	/* will hold a file descriptor */

char	*getoccupier(roomno);

int	roomno;

{	long	offset, lseek();

int	nread;

/* open되어있지 않으면 open()함 - 첫 번째 call인 경우*/

if (inf == -1) {

if ((inf = open("residents", O_RDONLY)) < 0)

return(NULLPTR);	/* could not open a file */

}

offset = (roomno - 1) * NAMELEN;

/* roomno에 있는 occupier's name을 읽음 */

if (lseek(inf, offset, 0) < 0)	return(NULLPTR);

nread = read(inf, namebuf, NAMELEN);

if (nread <= 0)

return(NULLPTR);	/* read() unsuccessful	*/

else {	namebuf[nread - 1] = '\0';	/* make a string	*/

return(namebuf);

}

}

□dup() and dup2()

- For file sharing in a process.

- An existing f.d. is duplicated into the lowest numbered available f.d.

int dup (int fd);

a new f.d.  an original f.d.

int dup2 (int oldfd, int newfd);

return 0 if O.K.

-1 otherwise

- A new f.d. shares the same file.

- Same as

newfd = fcntl (oldfd, F_DUPFD, req_value);

ex)	표준 출력의 방향 전환.

# ps > output

close(1);	/* stdout의 f.d. */

fd1=open("output", O_RDWR);

fd2=fcntl(fd1, F_DUPFD, 1);

0
fd2=1
	…		output
fd1
	…

□fcntl()

- Change the properties of an open file.

#include <fcntl.h>

int fcntl (int fd,     int cmd,      int arg);

작업종류(command) command에 따른 input value

p.42

ex)	fcntl (fd, F_DUPFD, 4)	/* fd의 f.t.값을 f.d. 4의 위치에 duplicate */

fcntl (fd, F_SETFL, FAPPEND);	/* file 끝에서부터 write */

□ioctl()

- Change the properties of an open device.

#include <sys/ioctl.h>

int ioctl (int fd, unsigned long request, char* arg);

command 종류	commnd value