/*
 http://sosal.tistory.com/
 * made by so_Sal
 */


fcntl - File control


#include <unistd.h>
#include <fcntl.h>

       int fcntl(int fd, int cmd);
       int fcntl(int fd, int cmd, long arg);
       int fcntl(int fd, int cmd, struct flock *lock);


(flock 구조체에 대해서는 게시하지 않았습니다. )

첫번째 매개변수 fd부터 보겠습니다.
fd - 파일 서술자(file descriptor)
      ㄴ 제어하기를 원하는 파일의 서술자를 지정하는 매개변수입니다.

리눅스 시스템에서 모든 프로세스는 0, 1, 2 라는 3개의 파일 서술자는 미리 정해져있습니다.
각각 입력(0), 출력(1), 에러(2)를 나타냅니다.
파일 서술자 리스트는 각 프로세스마다 독립적이며,
open() 함수를 이용해 파일을 가져오거나, 만들며
그 리턴값으로 파일 서술자 값(fd)을 얻을 수 있습니다.
또한, IPC 에서 쓰이는 pipe()나 socket()를 이용해서도 파일 서술자 값을 추가할 수 있습니다.





int
cmd :: 파일 서술자에게 원하는 명령을 넣는 매개변수입니다.

F_DUPFD  ::  long arg 인자보다 크기가 같거나, 같은 파일 서술자가 있다면 그보다 큰 값중에
                   가장 작은 파일 서술자 값을 찾아, 복사본을 만들어 준다.
                   복사될 파일 지정자를 사용자가 정해주는 dup(2)과는 엄밀히 다르다.
                   두 파일 서술자는 잠금(lock), 파일위치 포인터, flag 들을 공유한다.
                   (서술자중 하나에서 lseek 시스템콜에 의해 파일 위치가 변경되면,
                                                                          위치는 또다른 것에 변경될 수 있다.)
                   이 서술자들은 close-on-exec 플래그를 공유하지 않지만,
                   복제본의 close-on-exec 플래그는 off상태(실행중 종료되지 않는 상태) 가 된다.
                   return ::  새로운 기술자를 반환한다.

                   // close-on-exec란 ?
                   // 일반적으로 exec계열 함수는, 사용시 모든 파일 서술자를
                       새로 바뀔 프로그램에게 상속합니다.
                   // 기본적으로 off 되어있는 exec-on-exec 플래그가 on이 된다면
                       (1이 된다면) 상속되지 않음

F_GETFD  ::  close-on-exec 을 읽는다.
                   FD_CLOEXEC flag를 넘겨 받는다. 만일 FD_CLOEXEC 비트가 0일시,
                   파일은 exec 계열 함수를 사용할시 모든 서술자는 열린 상태로 상속되고,
                   그렇지 않으면 파일 서술자를 상속하지 않는다.
                   return :: Flag값을 반환한다.

F_SETFD  ::  close-on-exec 플래그를 사용자가 long arg 매개변수를 이용하여 설 정한다.

F_GETFL  ::  파일 서술자의 flag를 읽는다. (open(2) 에 의해 설정된 모든 플래그들을 리턴한다.)
                   return :: Flag값을 반환한다.

F_SETFL  ::  파일 서술자의 flag를 사용자가 long arg 매개변수를 이용하여 설정한다.
                   오직 O_APPEND, O_NONBLOCK, O_ASYNC 만이 설정될 수 있다.
                   다른 flag들은 변경할 수 없다. (ex : O_RDONLY, O_WRONLY.. 등)
                   (플래그 의미가 궁감하시다면 LINK_ 로.. )
                   dup(2), fork(2)로 만들어진 동일한 파일 기술자의 복사본들은 플래그를 공유한다.

    F_GETLK 와 F_SETLK는 임의의 파일에 잠금을 관리하는데 사용된다.
    원하는 잠금 설정을 사용할때, 3번째 인자에 flock 구조체의 포인터형을 넣는다.

F_GETLK  ::  잠금을 얻을 수 없도록 제어하는 flock 구조로 리턴하거나, 다른 장애가 없다면
                   잠금의 l_type 필드를 F_UNLCK 으로 설정한다.

F_SETLK  ::  l_type 이 F_RDLCK 혹은 F_WRLCK 일때, 잠금이 설정되고
                   l_type 이 F_UNLCK 일때는 잠금이 해제된다.
                   다른 누군가에 의해 서술자에 대한 lock이 설정되면 -1을 리턴한다.

F_SETLKW :: F_SETLK와 같지만, 에러를 리턴하는 대신 잠금이 풀리기를 기다린다.
                   (File _ Set _ lock _ wait 의 약자로 생각하면 쉽다.)
                   만일 fcntl 이 기다리고 있을때, 시그널이 잡히면 인터럽트 되고,
                   시그널 처리가 끝난 후에 F_SETLK와 동일하게 리턴한다.

      F_GETOWN, F_SETOWN, F_GETSIG, F_SETSIG 는 입출력 (I/O) 시그널을 관리하는데 사용한다.
      이들을 이용하여 select, poll 없이 비동기적 입출력을 구현할 수 있다.

F_GETOWN :: 파일 서술자 fd에서 일어나는 이벤트에 대한 SIGIO와, SIGURG 시그널을 수신하는
                    프로세스 ID or Group을 얻는다.
                    return :: 파일서술자 소유자의 값을 반환한다.

F_SETOWN  :: SIGIO, SIGURG 시그널을 수신하는 프로세스 ID or Group을 설정한다.

F_GETSIG  ::  입출력(I/O) 이 가능해질 때, 시그널이 전송되도록 한다.
                    제로값은 SIGIO가 전송되었음을 뜻하고, 다른값은 대신 보내진 시그널이다.
                     (SIGIO포함)
                   return :: 읽기,쓰기가 가능해졌을 때 전송된 시그널 값, SIGIO 행위에 대한 zero

F_SETSIG ::   입출력(I/O) 이 가능해질 때, 신호를 전송하도록 설정한다.
                    제로가 아닌 값으로 F_SETSIG를 사용하고, SA_SIGINFO (sigaction 참조 LINK_ )
                    를 설정하여 I/O 이벤트에 대한 입출력 정보가 siginfo_t 내로 전달되게 할 수 있다.
                    si_code 필드는 소스가 SI_SIGIO 라는 것을 가리키고,
                    si_fd 필드는 파일 서술자와 이벤트를 관련시킨다.



================================= 예제 =================================

/*
 *  아래에 있는 소스는 하나의 프로그램입니다.
 *  하나의 프로그램 안에 3개의 예제가 묶여져있는 형태입니다.
 *  내용이 바뀌는 부분은 슬러쉬 // ----- // 를 이용하여 구분하였습니다.
 */


#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>


int main(void){

    int fd;
    fd = open("test.txt", O_RDONLY | O_CREAT | O_APPEND , 0755);

//////////////////////////////////////////////////////////////////////////////////////

    int fd_dup1 = fcntl(fd,F_DUPFD,100);
    int fd_dup2 = fcntl(fd,F_DUPFD,100);

    printf("fd_dup1 :: %d\n",fd_dup1);    //100 출력
    printf("fd_dup2 :: %d\n",fd_dup2);    //101 출력
                                                       // F_DUPFD는 첫번째 인자를 복사하여, 3번째 매개변수로도
                                                       // 사용하게 끔 하는 함수이다. 3번째 인자와 같은 다른 서술자가
                                                       // 존재한다면, 그보다 큰것중에 가장 작은게 선택된다.

///////////////////////////////////////////////////
//execl 계열 함수를 사용할때에, 파일서술자를 상속하게 됩니다.
//하지만 close-on-exec가 on이 되어있다면 상속하지 않습니다.
//파일서술자 상속 여부를 설정하는 부분이 담긴 예제 입니다.

    int
flag = fcntl(fd, F_GETFD, 0);
    if( flag & FD_CLOEXEC )
        printf("close-on-exec on\n");
    else
        printf("close-on-exec off\n");

    fcntl(fd, F_SETFD, FD_CLOEXEC);       //F_SETFD를 이용하여
    flag = fcntl(fd, F_GETFD,0);                 //close-on-exec를 on으로 바꿉니다.
                                                           //더이상 파일서술자를 상속하지 않는 상태로 변경
    if( flag == FD_CLOEXEC )
        printf("close-on-exec on\n");
    else
        printf("close-on-exec off\n");

//////////////////////////////////////////////////

    int stat_flags;
    int access_flags;

    stat_flags = fcntl(fd, F_GETFL, 0);              //파일에 걸린 플레그들을 가져옵니다.
    access_flags = stat_flags & O_ACCMODE; // O_ACCMODE는 access flag들의 모든 합입니다.
                                                       // fd가 가리키는 파일이 가지는 모든 핸들과,
                                                       // O_ACCMODE를 앤드연산하면,
                                                       // 파일이 가지는 access flag들만 남겠죠?
                                                       // access flag는  O_RDONLY, O_WRONLY, O_RDWR을 말합니다.

    if (access_flags == O_RDONLY) printf("O_RDONLY");
    else if (access_flags == O_WRONLY) printf("O_WRONLY");
    else if (access_flags == O_RDWR) printf("O_RDWR");             //access flag 출력

    if (stat_flags & O_CREAT) printf(" | O_CREAT");
    if (stat_flags & O_EXCL) printf(" | O_EXCL");
    if (stat_flags & O_TRUNC) printf(" | O_TRUNC");
    if (stat_flags & O_APPEND) printf(" | O_APPEND");
    if (stat_flags & O_ASYNC) printf(" | O_ANSYC");
    if ((stat_flags & O_NONBLOCK) == O_NONBLOCK)
        printf(" | O_NONBLOCK");
    else
        printf(" | BLOCKING");
    printf("\n");

    return 0;
}

================================= 예제 =================================




(파일 이름이 real인건 의미 없습니다.)

파일 flag 부분에 나오는 결과에 의문이 있으신분이 있을것입니다. 
       fd = open("test.txt", O_RDONLY | O_CREAT | O_APPEND , 0755);
이렇게 파일 서술자를 열었는데, O_CREAT, O_APPEND는 왜 안뜨는것일까?
O_TRUNC (파일 초기화 시킨후 열기)도 flag에 추가해줘도 F_GETFL 으로 가져온
파일 서술자의 플레그 값에는 보이지 않습니다.
그 이유는, O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC 플래그는
다른 서술자가 파일을 가리킬 때 (혹은 열때), 그 방식을 지정하는 플래그 입니다.
파일 속성이 아니라 단순히 파일 서술자의 연산 (혹은 제어) 방식일 뿐이라는 말입니다.
따라서, 파일이 서술자에 의해 열리고 난 뒤에는 의미가 없는 플래그 이기 때문에,
F_GETFL로 얻어올 수 없습니다.

     :: 열려있는 파일의 속성과는 전혀 상관없는 플래그는 F_GETFL로 얻어올 수 없다.

Posted by sosal sosal

댓글을 달아 주세요

  1. 2010.10.13 22:43 신고

    글 잘 봤습니다:) 질문이 하나 생겼는데요,

    O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC 플래그는
    다른 서술자가 파일을 가리킬 때 (혹은 열때), 그 방식을 지정하는 플래그 입니다.
    파일 속성이 아니라 단순히 파일 서술자의 연산 (혹은 제어) 방식일 뿐이라는 말입니다.
    따라서, 파일이 서술자에 의해 열리고 난 뒤에는 의미가 없는 플래그 이기 때문에,
    F_GETFL로 얻어올 수 없습니다.

    이부분에서... 설명은 이렇게 해놓으시고 실제 예제코드에서는 CREAT등의 플래그도 if연산을 통해 출력을 하도록 되어 있는 이유는 무엇인가요?
    어차피 읽어오지 못하는 플래그라면... 굳이 코드에서 써질 필요가 없지 않나요??^^;

    • 2011.10.21 02:14 신고

      fd = open("test.txt", O_RDONLY | O_CREAT | O_EXCL , 0755);
      이렇게 파일 서술자를 열었는데, O_CREAT, O_EXCL는 왜 안뜨는것일까?
      O_TRUNC (파일 초기화 시킨후 열기)도 flag에 추가해줘도 F_GETFL 으로 가져온
      파일 서술자의 플레그 값에는 보이지 않습니다.
      ----------
      O_CREAT, O_TRUCN 같은경우 stat_flags에 남아있지 않음을 보여드리기 위해서 출력부분을 넣었습니다.

  2. 2010.10.13 22:45 신고

    아..한가지만 더 여쭙겠습니다~

    access_flags = stat_flags & O_ACCMODE

    이 부분은... O_ACCMODE를 굳이 &연산을 하지 않더라도 stat_flags가 이미 accessflag이라서.. 불필요해보이는데 이유가 무엇인가요?ㅎㅎ;

  3. 2010.10.20 01:19 신고

    아..답변을 이제야 보러 왔습니다.(실은 내일 시험 치거든요 ^^;)
    첫번째 질문에 대한 대답은 잘 이해가 되었습니다.
    두번째 질문에 대한 대답은 제가 좀더 조사?를 해본 결과
    O_ACCMODE는 일종의 마스크? 같은 역할을 하는 것으로써
    stat_flags에 들어오는 integer값과 O_ACCMODE에 있는 각종 integer 값들과 &연산을 하므로써 flag를 찾아낼 수 있다고 합니다.
    즉, stat_flags는 그냥 int값이기 때문에 그것만으로 O_RDONLY인지 다른 flag인지 알 수 없기 때문이라고 하네요 ㅎㅎㅎ;;

    많은 도움 받고 갑니다!

    • 2010.10.20 15:03 신고

      O_RDONLY :: 0
      O_O_WRONLY :: 1
      O_RDWR :: 2
      O_ACCMODE :: 3 -> O_RDONLY,O_WRONLY,O_RDWR의 합이네요
      stat_flags가 access_flags를 포함할 뿐만 아니라,
      다른 특성까지 모두 포함하는 플래그라서
      꼭 O_ACCOMODE와 and연산을 해야하네요.
      굳이 access_flags를 만들지 않겠다면,

      switch(stat_flags & O_ACCOMODE){
        case0:
          printf("O_RDONLY");
          break;
        case1:
          printf("O_WRONLY");
          break;
        case2:
          printf("O_RDWR");
          break;
      }
      이런식으로 구현하면 되겠네요. 사실 거의 말장난이지만;;;
      덕분에 저도 공부했습니다 ㅎㅎ