리눅스 시스템의 파일 디스크립터, I/O, 터미널 설정을 control하는 법을 알아보자.
함수원형
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
int result = fcntl(int fd, int cmd); //basic
int result = fcntl(int fd, int cmd, long arg);
int result = fcntl(int fd, int cmd, struct flock *lockp);
//work : Control file descriptors
//return : -1 (error), Other (depends on cmd)
fcntl ARGS:
int fd : command를 수행하고 싶은 file의 descriptor
int cmd : 수행할 command (fcntl.h에 상수로 정의)
long arg, struct flock* lockp : command에 따라 추가되는 arg
fcntl에서 자주 사용하는 cmd
F_GETFL: fd에 대한 파일 상태 속성들을 반환값으로 돌려줌.
F_SETFL: 파일 상태 속성들을 세번째 인수로 받아 설정. O_APPEND, O_NONBLOCK, O_SYNC, O_DSYNC, O_RSYNC, O_FSYNC, O_ASYNC으로 속성만 변경할 수도 있다.
여러 프로세스가 resource에 동시에 공유(접근)하는 경우에 발생하는 문제로, fcntl로 file을 Auto-append mode로 설정하여 해결할 수 있다.
Auto-append mode
리눅스에서 파일을 여는 방식 중 하나이다.
1. 여러 프로세스가 동시에 파일에 데이터를 쓰려고 할 때, 원자성을 보장한다.
2. 파일에 데이터를 쓸 때마다, 커서 위치에 상관없이 항상 파일 끝에 데이터를 추가하게 됩니다.
#include <fcntl.h>
int main()
{
...
int s;
s = fcntl(fd, F_GETFL); //fd의 file setting 정보를 가져옴
s |= O_APPEND; //O_APPEND mode를 추가
result = fcntl(fd, F_SETFL, s); //바뀐 설정 set
if(result == -1)
perror(“setting APPEND”);
...
}
함수원형
#include <sys/ioctl.h>
int result = ioctl(int fd, int operation (, arg...) );
//work: 입출력 장치 제어
//return: -1(error), other(depend on operation)
ioctl ARGS:
int fd: operation을 적용할 device의 file descriptor
int operation: 적용할 Operation(ioctl.h에 상수로 정의)
ioctl의 fd를 0으로 사용하기(kick)
프로그램을 실행할 때 사용하는 표준 입력(stdin, 즉 fd = 0)이 터미널과 연결되어 있기 때문에, 이를 통해서도 터미널 설정을 변경이 가능하다.
따라서, tcgetattr이나 ioctl에서 stdin 파일 디스크립터를 사용하면, 현재 터미널과 연결된 디바이스의 속성을 제어할 수 있다. 이는 프로세스가 터미널에서 입력을 받고 출력을 처리하기 위해 터미널 장치 파일에 연결되어 있기 때문에 가능하다.
->이 때문에 별도로 open("/dev/pts/[number]")로 device 파일을 열지 않고도, 표준 입력 파일 디스크립터를 통해 터미널 속성에 접근할 수 있다.
tcgetattr 함수원형
#include <termios.h>
#include <unistd.h>
int result = tcgetattr(int fd, struct termios *info);
//work: 터미널 정보를 read
//return: -1 (error), other(depends on dev)
struct termios
{
tcflag_t c_iflag; /* input mode flags */
tcflag_t c_oflag; /* output mode flags */
tcflag_t c_cflag; /* control mode flags */
tcflag_t c_lflag; /* local mode flags */
cc_t c_cc[NCCS]; /* control charters */
speed_t c_ispeed; /* input speed */
speed_t c_ospeed; /* output speed */
};
tcgetattr ARGS:
int fd: 읽어올 terminal device file descriptor
struct termios* info: terminal 정보를 받아올 termios 버퍼
#include <termios.h>
#include <unistd.h>
int result = tcsetattr(int fd, int when, struct termios *info);
//work: 변경한 터미널 정보를 set
//return: -1(error), 0(succuess)
tcsetattr ARGS:
int fd: 적용할 terminal device file descriptor
struct termios* info: terminal 정보를 적용할 termios 버퍼
int when: 터미널 세팅을 언제 변경할 것인가에 대한 정보. 보통 TCSANOW 상수를 사용한다.
ioctl을 사용하여 현재 터미널의 i/o size를 구한 뒤, tcgetattr, tcsetattr을 사용하여 Canonical mode와 bloking mode의 문제점을 해결한 리눅스 시스템 명령어 'more'을 구현한 것이 있으니 참고하면 좋다.