Name

recv, recvfrom, recvmsg - receive a message from a socket

Synopsis

#include <sys/types.h>
#include <sys/socket.h>

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                 struct sockaddr *src_addr, socklen_t *addrlen);

ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

Description

The recvfrom() and recvmsg() calls are used to receive messages from a socket, and may be used to receive data on a socket whether or not it is connection-oriented.

If src_addr is not NULL, and the underlying protocol provides the source address, this source address is filled in. When src_addr is NULL, nothing is filled in; in this case, addrlen is not used, and should also be NULL. The argument addrlen is a value-result argument, which the caller should initialize before the call to the size of the buffer associated with src_addr, and modified on return to indicate the actual size of the source address. The returned address is truncated if the buffer provided is too small; in this case, addrlen will return a value greater than was supplied to the call.

The recv() call is normally used only on a connected socket (see connect(2)) and is identical to recvfrom() with a NULL src_addr argument.

All three routines return the length of the message on successful completion. If a message is too long to fit in the supplied buffer, excess bytes may be discarded depending on the type of socket the message is received from.

If no messages are available at the socket, the receive calls wait for a message to arrive, unless the socket is nonblocking (see fcntl(2)), in which case the value -1 is returned and the external variable errno is set to EAGAIN or EWOULDBLOCK. The receive calls normally return any data available, up to the requested amount, rather than waiting for receipt of the full amount requested.

The select(2) or poll(2) call may be used to determine when more data arrives.

The flags argument to a recv() call is formed by ORing one or more of the following values:

MSG_CMSG_CLOEXEC (recvmsg() only; since Linux 2.6.23)
Set the close-on-exec flag for the file descriptor received via a UNIX domain file descriptor using the SCM_RIGHTS operation (described in unix(7)). This flag is useful for the same reasons as the O_CLOEXEC flag of open(2).
MSG_DONTWAIT (since Linux 2.2)
Enables nonblocking operation; if the operation would block, the call fails with the error EAGAIN or EWOULDBLOCK (this can also be enabled using the O_NONBLOCK flag with the F_SETFL fcntl(2)).
MSG_ERRQUEUE (since Linux 2.2)
This flag specifies that queued errors should be received from the socket error queue. The error is passed in an ancillary message with a type dependent on the protocol (for IPv4 IP_RECVERR). The user should supply a buffer of sufficient size. See cmsg(3) and ip(7) for more information. The payload of the original packet that caused the error is passed as normal data via msg_iovec. The original destination address of the datagram that caused the error is supplied via msg_name.
For local errors, no address is passed (this can be checked with the cmsg_len member of the cmsghdr). For error receives, the MSG_ERRQUEUE is set in the msghdr. After an error has been passed, the pending socket error is regenerated based on the next queued error and will be passed on the next socket operation.

The error is supplied in a sock_extended_err structure:

#define SO_EE_ORIGIN_NONE    0
#define SO_EE_ORIGIN_LOCAL   1
#define SO_EE_ORIGIN_ICMP    2
#define SO_EE_ORIGIN_ICMP6   3

struct sock_extended_err
{
    uint32_t ee_errno;   /* error number */
    uint8_t  ee_origin;  /* where the error originated */
    uint8_t  ee_type;    /* type */
    uint8_t  ee_code;    /* code */
    uint8_t  ee_pad;     /* padding */
    uint32_t ee_info;    /* additional information */
    uint32_t ee_data;    /* other data */
    /* More data may follow */
};

struct sockaddr *SO_EE_OFFENDER(struct sock_extended_err *);
ee_errno contains the errno number of the queued error. ee_origin is the origin code of where the error originated. The other fields are protocol-specific. The macro SOCK_EE_OFFENDER returns a pointer to the address of the network object where the error originated from given a pointer to the ancillary message. If this address is not known, the sa_family member of the sockaddr contains AF_UNSPEC and the other fields of the sockaddr are undefined. The payload of the packet that caused the error is passed as normal data.

For local errors, no address is passed (this can be checked with the cmsg_len member of the cmsghdr). For error receives, the MSG_ERRQUEUE is set in the msghdr. After an error has been passed, the pending socket error is regenerated based on the next queued error and will be passed on the next socket operation.

MSG_OOB
This flag requests receipt of out-of-band data that would not be received in the normal data stream. Some protocols place expedited data at the head of the normal data queue, and thus this flag cannot be used with such protocols.
MSG_PEEK
This flag causes the receive operation to return data from the beginning of the receive queue without removing that data from the queue. Thus, a subsequent receive call will return the same data.
MSG_TRUNC (since Linux 2.2)
For raw (AF_PACKET), Internet datagram (since Linux 2.4.27/2.6.8), and netlink (since Linux 2.6.22) sockets: return the real length of the packet or datagram, even when it was longer than the passed buffer. Not implemented for UNIX domain (unix(7)) sockets.

For use with Internet stream sockets, see tcp(7).

MSG_WAITALL (since Linux 2.2)
This flag requests that the operation block until the full request is satisfied. However, the call may still return less data than requested if a signal is caught, an error or disconnect occurs, or the next data to be received is of a different type than that returned.
The recvmsg() call uses a msghdr structure to minimize the number of directly supplied arguments. This structure is defined as follows in <sys/socket.h>:
struct iovec {                    /* Scatter/gather array items */
    void  *iov_base;              /* Starting address */
    size_t iov_len;               /* Number of bytes to transfer */
};

struct msghdr {
    void         *msg_name;       /* optional address */
    socklen_t     msg_namelen;    /* size of address */
    struct iovec *msg_iov;        /* scatter/gather array */
    size_t        msg_iovlen;     /* # elements in msg_iov */
    void         *msg_control;    /* ancillary data, see below */
    size_t        msg_controllen; /* ancillary data buffer len */
    int           msg_flags;      /* flags on received message */
};
Here msg_name and msg_namelen specify the source address if the socket is unconnected; msg_name may be given as a NULL pointer if no names are desired or required. The fields msg_iov and msg_iovlen describe scatter-gather locations, as discussed in readv(2). The field msg_control, which has length msg_controllen, points to a buffer for other protocol control-related messages or miscellaneous ancillary data. When recvmsg() is called, msg_controllen should contain the length of the available buffer in msg_control; upon return from a successful call it will contain the length of the control message sequence.

The messages are of the form:

struct cmsghdr {
    socklen_t     cmsg_len;     /* data byte count, including hdr */
    int           cmsg_level;   /* originating protocol */
    int           cmsg_type;    /* protocol-specific type */
/* followed by
    unsigned char cmsg_data[]; */
};
Ancillary data should only be accessed by the macros defined in cmsg(3).

As an example, Linux uses this ancillary data mechanism to pass extended errors, IP options, or file descriptors over UNIX domain sockets.

The msg_flags field in the msghdr is set on return of recvmsg(). It can contain several flags:

MSG_EOR
indicates end-of-record; the data returned completed a record (generally used with sockets of type SOCK_SEQPACKET).
MSG_TRUNC
indicates that the trailing portion of a datagram was discarded because the datagram was larger than the buffer supplied.
MSG_CTRUNC
indicates that some control data were discarded due to lack of space in the buffer for ancillary data.
MSG_OOB
is returned to indicate that expedited or out-of-band data were received.
MSG_ERRQUEUE
indicates that no data was received but an extended error from the socket error queue.

Return Value

These calls return the number of bytes received, or -1 if an error occurred. The return value will be 0 when the peer has performed an orderly shutdown.

Errors

These are some standard errors generated by the socket layer. Additional errors may be generated and returned from the underlying protocol modules; see their manual pages.

EAGAIN or EWOULDBLOCK
The socket is marked nonblocking and the receive operation would block, or a receive timeout had been set and the timeout expired before data was received. POSIX.1-2001 allows either error to be returned for this case, and does not require these constants to have the same value, so a portable application should check for both possibilities.
EBADF

The argument sockfd is an invalid descriptor.

ECONNREFUSED
A remote host refused to allow the network connection (typically because it is not running the requested service).
EFAULT

The receive buffer pointer(s) point outside the process's address space.

EINTR

The receive was interrupted by delivery of a signal before any data were available; see signal(7).

EINVAL

Invalid argument passed.

ENOMEM

Could not allocate memory for recvmsg().

ENOTCONN
The socket is associated with a connection-oriented protocol and has not been connected (see connect(2) and accept(2)).
ENOTSOCK
The argument sockfd does not refer to a socket.

Conforming To

4.4BSD (these function calls first appeared in 4.2BSD), POSIX.1-2001.

POSIX.1-2001 only describes the MSG_OOB, MSG_PEEK, and MSG_WAITALL flags.

Notes

The prototypes given above follow glibc2. The Single UNIX Specification agrees, except that it has return values of type ssize_t (while 4.x BSD and libc4 and libc5 all have int). The flags argument is int in 4.x BSD, but unsigned int in libc4 and libc5. The len argument is int in 4.x BSD, but size_t in libc4 and libc5. The addrlen argument is int * in 4.x BSD, libc4 and libc5. The present socklen_t * was invented by POSIX. See also accept(2).

According to POSIX.1-2001, the msg_controllen field of the msghdr structure should be typed as socklen_t, but glibc currently types it as size_t.

See recvmmsg(2) for information about a Linux-specific system call that can be used to receive multiple datagrams in a single call.

Example

An example of the use of recvfrom() is shown in getaddrinfo(3).

See Also

fcntl(2), getsockopt(2), read(2), recvmmsg(2), select(2), shutdown(2), socket(2), cmsg(3), sockatmark(3), socket(7)

Referenced By

ddp(7), getifaddrs(3), netlink(7), packet(7), raw(7), rds(7), rds-rdma(7), sctp(7), socketcall(2), syscalls(2), udp(7)


readv와 writev 함수도 효율성을 향샹시키는데 도움이 되는 함수들이다. 일단 사용 방법부터

살펴보고 어떠한 경우에 사용되는지 알아보도록 하자.

이 함수들은 한 마디로 표현하면 "데이터를 모아서 전송하고 데이터를 나누어서 수신하는 함수"

라고 말할 수 있다. 즉 writev 함수를 사용하게 되면 여러 버퍼에 저장되어 있는 데이터를 한 번에

전송하게 되고, 또 readv 함수를 사용하면 데이터를 여러 버퍼에 나누어서 수신할 수 있게 된다.


------------- writev 함수 선언 -------------------

#include <sys/uio.h>

int writev(int fd, const struct iovec *vector, int count);

리턴값 : 성공시 전송한 바이트 수, 실패시 -1 리턴

인자값 :

 - fd : 데이터 전송의 목적지를 나타내는 소켓의 파일 디스크립터를 전달한다.

        반드시 소켓에만 제한되는 함수가 아니다. read, write 함수처럼 파일이나

         콘솔을 입, 출력 대상으로 할 수도 있다.

 - vector : 일반적으로 iovec 구조체 배열의 이름을 인자로 전달하는데, iovec 구조체에는

          전송하고자 하는 데이터에 대한 정보가 담겨진다.

 - count : 데이터를 전송하기 위해 참고할 iovec 구조체 변수의 수를 지정한다. 만약에 3이 인자로
    
             전달되면, vector가 가리키는 iovec 구조체 변수를 시작으로 총 3개의 iovec 변수를 참고하여

 데이터를 전송하게 된다.

------------------------------------------------

----------------- iovec는 구조체의 선언 ------------

struct iovec
{
 ptr_t iov_base;
 size_t iov_len;
};

iov_base : 전송할 데이터의 시작 주소를 가리킨다.

iov_len : iov_base가 가리키는 위치를 시작으로 전송하고자 하는 바이트 수를 대입한다.

            뒤에나오는 readv 함수에서는 수신하고자 하는 최대 바이트 수를 나타내는 변수로 그의미가

          달라진다.

-------------------------------------------------------

 

-------------- writev 함수 사용 예제 -------------------
#include <sys/uio.h>
#include <stdio.h>
#include <string.h>

int main()
{
        struct iovec iov[2] = {0};
        char buf1[] = "Hello!!! ";
        char buf2[] = "I am Hacker!!\n";

        iov[0].iov_base = buf1;
        iov[0].iov_len = strlen(buf1);

        iov[1].iov_base = buf2;
        iov[1].iov_len = strlen(buf2);

        writev(1,iov,2);

        return 0;
}
-------------------------------------------------------

 

-------------------------------------------------

 

[결과화면]

 

[root@localhost readv_writev]# ./writev.exe
Hello!!! I am Hacker!!
--------------------------------------------------------


이번에는 readv 함수에 대해 살펴보자. readv 함수는 writev 함수의 인자와 인자 값의

의미를 반대로 생각하면된다.

--------------------- readv 함수 선언 ----------------
#include <sys/uio.h>

int readv(int fd, const struct iovec *vector, int count);

리턴 값 : 성공시 수신한 바이트 수, 실패 시 -1 리턴

인자값 :
   - fd : 데이터를 수신할 파일(혹은 소켓)의 파일 디스크립터를 인자로 전달한다.

   - vector : writev 함수에서 사용되던 용도와 비슷화다. 어디에다가 얼마큰 데이터를 수신할 것인에
 대한 정보를 iovec 구조체 변수에다가 넣어서 그 배열의 이름을 인자로 전달한다.

   - count : 데이터를 수신하기 위해서 참고할 iovec 구조체 변수의 수를 지정한다. 만약에 3이 인자로
 들어가면 vector가 가리키는 iovec 구조체 변수를 시작으로 총 3개의 iovec 변수를 참고하여
 데이터를 수신하게 된다.

----------------------------------------------------------------------

--------------------- readv 사용 예제 -------------------

#include <stdio.h>
#include <sys/uio.h>


int main()
{
        struct iovec iov[2];
        char buf1[10] = {0};
        char buf2[10] = {0};
        int strLen = 0;


        iov[0].iov_base = buf1;
        iov[0].iov_len = sizeof(buf1) - 1;

        iov[1].iov_base = buf2;
        iov[1].iov_len = sizeof(buf2) - 1;

        strLen = readv(0,iov,2);
        printf("total : %d\n",strLen);
        printf("buf1 : %s\n",buf1);
        printf("buf2 : %s\n",buf2);

 


        return 0;
}

--------------------------------------------------------------------------------

 
------------------------------------------------------------------------------------

 

[결과화면]

 

[root@localhost readv_writev]# ./readv.exe
I am Hacker!!
total : 14
buf1 : I am Hack
buf2 : er!!
------------------------------------------------------------------------------------

 

[ readv & writev 함수의 적절한 사용 ]

 

어떠한 경우가 readv와 writev 함수를 적절히 사용한 예에 해당할까? 사용할 수 있는 모든 경우가

다 적절한 사용의 예라고 한다.. (뭐 지.. ㅋ)

즉 전송해야 할 데이터가 여러 개의 배열에 나뉘어서 저당되어 있는 경우에는, 모두 전송하기 위해서

write 함수를 여러 번 호출해야 하는데, 이것보다는 한 번의 writev 함수 호출이 더  효율적이다.

또한 입력 버퍼에 수신된 데이터를 여러 배열에 나누어서 읽어 들이고 싶은 경우에도

read 함수를 여러 번 호출하는 것보다 readv 함수를 여러번 호출하는 것이 더 효율적이다.

그렇다면 어떤 이유에서 보다 효율적이라고 말할 수 있는가 ?

일단 간단하게 C언어의 차원에서 생각해 봐도, 함수 호출이 적으면 그만큼 성능 향상을 가져오므로

이득일 것이다. 그러나  전송되는 패킷의 수를 줄일 수 있다는 것이 더큰 이유가 된다.

서버의 성능 향상을 위해서 Nagle 알고리즘을 사용을 오프(Off : 사용하지 않음) 시켰을 때를

예로 들어보자. 사실 writev 함수는 Nagle 알고리즘을 오프(Off)시켰을 때 더 사용가치가 높다.

한 번에 전송하고 싶은 데이터가 있는데 모두 세곳의 배열에 나누어 저장되어 있다고 가정해 보자

모두 다 전송하기 위해서는 세 번의 write 함수를 호출해야만 한다. 그런데 속도 향상을 목적으로

이미 Nagle 알고리즘이 오프(Off)되어 있었다면 총 몇 개의 패킨이 생성되어 전송되겠는가?

Nagle 알고리즘을 적용하지 않는 경우에는 출력 버퍼에 데이터가 들어오자 마자 데이터를 블록화

해서 전송하기 때문에 총 세 개의 패킷이 생성되어 전송된다. 여기서 우리는 한꺼번에 블록화해서

 전송해도 되는 데이터를 세 개의 패킷으로 나누어 보낸 것이 된다. 이는 전체 네트워크 입장에서도

수신하는 호스트 입장에서도 그리 좋은 결과가 아니다. 같은 양의 데이터를 전송하더라도

나누어서 전송하게 될 경우에는 전송되는 헤더의 수가 그 만큼 많이지기 때문에 네트워크 입장에서는

트래픽이 증가하게되고, 수신하는 호스트 입장에서는 처리해야하는 패킷의 수가 증가하게 된다.

이러한 경우에 write함수를 여러 번 호출 하는 것 대신에, writev 함수를 사용하게 되면

여러 배열에 저장되어있던 데이터를 하나의 패킷에 모두 담아서 한 번에 전송할 수 있 게된다.

비록 Nagle 알고리즘이 동작하고 있는 상태라도 말이다.

한 가지 더 댕각해 볼것은 여러 배열에 나뉘어 있는 데이터를 모두 담을 수 있는 큰 배열을 생성해서

그 안에다가 작은 배열에 존재하는 데이터를 차례로 복사한 다음에 write 함수를 호출해서

한 번에 전송하면 어떨까 하는 것이다. writev 함수를 호출한 것은 같은 효과를 얻을 수 있지 않을까?

그렇다 그러나 그것보다는 writev 함수를 사용하는 것이 더 편리하다. 뿐만 아니라.

불필요하게 큰 배열을 할당할 필요도 없게 된다.

자! 다시 결론은 하나다. writev 함숨와 readv 함수를 사용할 상황이 되면 적극 사용하도록 하자.


 

설명

파일 디스크립터 복사본을 만듭니다. dup()는 커널에서 알아서 사용하지 않는 디스크립터 번호 중에 하나가 자동으로 지정되지만 dup2()는 프로그래머가 원하는 번호로 지정할 수 있습니다.

프로그래머가 지정하는 번호가 이미 사용하는 번호라면 dup2()는 자동으로 그 파일을 닫고 다시 지정해 줍니다.

헤더 unistd.h
형태 int dup2(int fildes, int fildes2);
인수 int fildes 파일 디스크립터
  int fildes2 원하는 파일 디스크립터 번호
반환 int 원하는 파일 디스크립터 번호, 실패하면 -1 이 반환됩니다.
예제
#include <stdio.h>         // puts()
#include <string.h>        // strlen(), memset()
#include <fcntl.h>         // O_RDWR, O_CREAT
#include <unistd.h>        // write(), close(), dup()

#define  BUFF_SIZE   1024

int main()
{
   int   fd_wr;
   int   fd_rd;
   char  buff[BUFF_SIZE];
   char *str   = "forum.falinux.com";

   fd_wr = open( "./test.txt", O_RDWR ¦ O_CREAT ¦ O_TRUNC, 0644);
   fd_rd = dup2( fd_wr, 100);
   printf( "fd_wr= %d  fd_rd= %d\n", fd_wr, fd_rd);          // 디스크립터 번호 확인

   write(  fd_wr, str, strlen( str));
   close( fd_wr);                        // fd_wr 닫음

   lseek( fd_rd, 0, SEEK_SET);
   memset( buff, '\0', BUFF_SIZE);
   read( fd_rd, buff, BUFF_SIZE);

   printf( "%s\n", buff);

   close( fd_rd);                        // fd_rd 닫음

   return 0;
}
]$ ./a.out
fd_wr= 3  fd_rd= 100
forum.falinux.com
]$


설명

파일 디스크립터 복사본을 만듭니다. 원본 디스크립터와 복사된 디스크립터의 읽기/쓰기 포인터는 공유됩니다. 즉, 원본과 복사본 디스크립터마다 따로 읽기/쓰기 포인터가 존재하지 않습니다.

헤더 unistd.h
형태 int dup(int fildes);
인수 int fildes 파일 디스크립터
반환 int 복사된 파일 디스크립터 번호로 사용되지 않은 가장 작은 번호가 자동으로 지정되어 반환됩니다.
함수 실행이 실패되면 -1 이 반환됩니다.
예제
#include <stdio.h>         // puts()
#include <string.h>        // strlen(), memset()
#include <fcntl.h>         // O_RDWR, O_CREAT
#include <unistd.h>        // write(), close(), dup()

#define  BUFF_SIZE   1024

int main()
{
   int   fd_wr;
   int   fd_rd;
   char  buff[BUFF_SIZE];
   char *str   = "forum.falinux.com";

   fd_wr = open( "./test.txt", O_RDWR ¦ O_CREAT ¦ O_TRUNC, 0644);
   fd_rd = dup( fd_wr);

   write(  fd_wr, str, strlen( str));

   close( fd_wr);                        // fd_wr 닫음

   lseek( fd_rd, 0, SEEK_SET);
   memset( buff, '\0', BUFF_SIZE);
   read( fd_rd, buff, BUFF_SIZE);

   printf( "%s\n", buff);

   close( fd_rd);                        // fd_rd 닫음

   return 0;
}
]$ ./a.out
forum.falinux.com
]$


설명

복사할 문자열 크기에 맞는 메모리를 확보한 후 문자열을 복사한 후, 확보한 메모리의 포인터를 반환해 줍니다.

헤더 string.h
형태 char * strdup( const char *str);
인수
char *str 복사할 문자열
반환 확보된 문자열 메모리의 첫 주소를 반환

예제

#include <stdio.h>
#include <string.h>

int main( void)
{
   char *ptr;
  
   ptr = strdup( "forum.falinux.com");
   printf( "%s\n", ptr);

   return 0;
}
]$ ./a.out
forum.falinux.com


설명

fcntl() 함수는 파일을 전체나 파일의 일부를 다른 프로세스에서 사용하는 것을 제한할 수 있도록 해 주는 함수입니다. 물론 파일을 사용하기 위해 open() 함수나 fopen() 함수의 mode를 이용하여 다른 프로세스가 읽기나 쓰기를 제한할 수 있습니다. 그러나 이것은 파일 전체에 대해 적용되며 제한 내용을 변경하기 위해서는 파일을 닫았다가 다시 열기를 해야 합니다.

fcntl()은 열려진 파일에 대해서 필요에 따라 제한을 여러 번 변경하실 수 있으며, 파일 전체 뿐만 아니라 일부를 제한, 즉 "잠금" 상태를 만들 수 있기 때문에 fcntl() 함수를 "파일 잠금 함수"라기 보다는 "레코드 잠금 함수"로 불리워 집니다.

*** 주의하실 내용은 *** 파일에 대한 잠금은 프로세스별로 잠금 정보를 지정하는 것 뿐이지 실제로 다른 프로세스가 읽고 쓰는 것을 못하게 하는 것은 아닙니다. 즉, 쓰기를 제한했다고 해서 다른 프로세스에서 write() 가 실행이 안 되거나 에러가 발생하지 않습니다.

잠금 정보는 하나의 파일을 여러 프로세스가 동시에 사용하는 경우, 같은 시간에 쓰기를 하거나 아직 한쪽에서 변경 중이라면 다른 프로세스가 읽지를 못하게 하기 위한 정보를 주고 받기 위한 방법으로 이해햐셔야 합니다.

아래 예제에서도 쓰기를 하기 전에 쓰기 잠금이 가능한지를 확인한 후에 write() 를 실행했습니다. 그러나 확인없이 쓰기를 한다면 쓰기가 가능합니다. 그러므로 잠금 정보는 프로세스가 쓰기를 못하게 한다가 아니라 지금은 읽어 서는 안 된다 또는 쓰기를 해서는 안 된다라는 정보로 이용하셔야 합니다.

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

cmd에는 아래와 같이 상수로 정의되어 있습니다.

cmd 의미
F_GETLK 레코의 잠금 상태를 구해지며, 정보는 세번째 인수인 lock에 담겨져 옮니다.
F_SETLK 레코드 잠금을 요청하며, 다른 프로세스가 먼저 선점해서 실패했다면 즉시 -1 로 복귀합니다.
F_SETLKW 끝의 W는 wait의 약자로 레코드 잠금을 요청했는데, 다른 프로세스가 먼저 선점해서 실패했다면 그 프로세스가 해제할 때까지 대기합니다.

이번에는 struct flock * lock의 내용을 알아 봐야 겠지요. ^^

struct flock {
        short   l_type;
        short   l_whence;
        off_t   l_start;
        off_t   l_len;
        pid_t   l_pid;
        __ARCH_FLOCK_PAD
};

l_type 은 어떻게 잠금을 할지, 해제할지를 정합니다. 즉, 아래와 같은 상수가 정의되어 있습니다.

l_type 의미
F_RDLCK 다른 프로세스가 읽기 잠금만 가능하게하고 쓰기 잠금은 못하게 합니다.
F_WRLCK 다른 프로세스는 읽기 잠금과 쓰기 잠금 모두 불가능하도록 합니다.
F_UNLCK 잠금을 해제합니다.

l_whence 는 블록할 영역을 지정하는 기준 위치를 지정합니다. 즉, 파일 첫 부분부터 따질지, 아니면 현제 읽기/쓰기 포인터를 기준으로 따질지를 정합니다.

l_whence 의미
SEEK_SET 파일의 시작 위치
SEEK_CUR 현재 읽기/쓰기 포인터를 기준
SEEK_END 파일의 끝을 기준

l_startl_len은 l_whence가 가르키는 위치에서 블록을 지정합니다. 또한 l_len이 0 의 값이 되면 l_len값은 l_start부터 파일 끝 사이의 크기가 됩니다.

즉,

  • l_whence가 SEEK_CUR이고
  • l_start 가 0이면서
  • l_len 이 0 이면

파일 전체를 가르키게 됩니다. 아래의 그림을 보십시오.

l_pid 는 F_GETLK 를 실행하여 레코드에 대한 잠금 상태 정보를 구할 때, 이미 잠금을 실행하고 있는 프로세스의 ID 입니다.

헤더 unistd.h, fcntl.h
형태 int fcntl(int fd, int cmd, struct flock * lock);
인수
int fd 제어 대상 파일 디스크립터
int cmd 제어 동작 명령
struct flock * lock 잠금을 위한 옵션
반환
-1 실패
-1 != cmd에 따라 달라짐

예제 1

예제에서는 같은 파일을 부모 프로세스가 열기를 하고 7번째 문자부터 7개의 문자 영역을 읽기만 가능할 뿐 쓰기를 해서는 안된다는 잠금 정보를 설정합니다. 차일드 프로세스는 쓰기를 할 때, 변경하려는 영역이 과연 쓰기가 가능한 것인지를 확인한 후에 쓰기를 합니다.

예제에서 차일드는 2 곳에 따로따로 쓰기를 하는데, 한 번은 쓰기가 가능한 곳이지만 다른 한 곳은 부모 프로세스에 의해 읽기 잠금한 영역입니다. 이 때 쓰기가 어떻게 되는지 보겠습니다.

예제에서 사용하는 test.txt의 내용은 "FORUM.FALINUX.COM HAPPY NEW YEAR!!" 입니다.

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

int main()
{
   char    *filename = "./test.txt";
   int      fd;
   pid_t    pid;
   struct   flock filelock;

   pid   = fork();
   switch( pid)
   {
      case -1  :
      {
         printf( "자식 프로세스 생성 실패\n");
         return -1;
      }
      case 0   :
      {
         printf( "자식: 부모 프로세스를 위해 잠시 대기하겠습니다.\n");
         sleep( 1);

         printf( "자식: 파일의 내용을 수정합니다.\n");
         fd = open( filename, O_RDWR ¦ O_CREAT, 0666);

         filelock.l_type   = F_WRLCK;
         filelock.l_whence = SEEK_SET;
         filelock.l_start  = 0;
         filelock.l_len    = 6;

         if ( -1 == fcntl( fd, F_SETLK, &filelock))
         {
            printf( "자식:레코드 잠금에 실패해서 forum을 쓰지를 못했습니다.\n");
         }
         else
         {
            write( fd, "forum", 6);
         }

         filelock.l_type   = F_WRLCK;
         filelock.l_whence = SEEK_SET;
         filelock.l_start  = 6;
         filelock.l_len    = 7;

         if ( -1 == fcntl( fd, F_SETLK, &filelock))
         {
            // 여기서는 이 If 절을 만족하게 됩니다.
            printf( "자식:레코드 잠금에 실패해서 falinux를 쓰지 못했습니다.\n");
         }
         else
         {
            write( fd, "falinux", 7);
         }

         close( fd);
         break;
      }
      default  :
      {
         printf( "부모: 파일을 열고 레코드 잠금하겠습니다.\n");
         fd = open( filename, O_RDWR, 0666);

         filelock.l_type   = F_RDLCK;
         filelock.l_whence = SEEK_SET;
         filelock.l_start  = 7;
         filelock.l_len    = 7;

         if ( -1 != fcntl( fd, F_SETLK, &filelock))
         {
            printf( "부모: 잠금에 성공했으며 3초간 대기합니다.\n");
            sleep( 3);
         }
         close( fd);
      }
   }
}
]$ ./a.out
]$ cat test.txt
FORUM.FALINUX.COM HAPPY NEW YEAR!!          // 모두 대문자
]$ ./a.out
자식: 부모 프로세스를 위해 잠시 대기하겠습니다.
부모: 파일을 열고 레코드 잠금하겠습니다.
부모: 잠금에 성공했으며 3초간 대기합니다.
자식: 파일의 내용을 수정합니다.
자식: 레코드 잠금에 실패해서 falinux를 쓰지 못했습니다.
]$ cat test.txt
forumFALINUX.COM HAPPY NEW YEAR!!          // 역시 첫번째 쓰기만 적용되었습니다.
]$

예제 2

예제 1은 cmd를 F_SETLK를 사용했지만 이번에는 잠금 상태가 해제될 때까지 기다리는 F_SETLKW를 사용해 보겠습니다.

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

int main()
{
   char    *filename = "./test.txt";
   int      fd;
   pid_t    pid;
   struct   flock filelock;

   pid   = fork();
   switch( pid)
   {
      case -1  :
      {
         printf( "자식 프로세스 생성 실패\n");
         return -1;
      }
      case 0   :
      {
         printf( "자식: 부모 프로세스를 위해 잠시 대기하겠습니다.\n");
         sleep( 1);

         printf( "자식: 파일의 내용을 수정합니다.\n");
         fd = open( filename, O_RDWR ¦ O_CREAT, 0666);

         filelock.l_type   = F_WRLCK;
         filelock.l_whence = SEEK_SET;
         filelock.l_start  = 0;
         filelock.l_len    = 6;

         if ( -1 == fcntl( fd, F_SETLKW, &filelock))
         {
            printf( "레코드 잠금에 실패해서 forum을 쓰지를 못했습니다.\n");
         }
         else
         {
            write( fd, "forum", 6);
         }

         filelock.l_type   = F_WRLCK;
         filelock.l_whence = SEEK_SET;
         filelock.l_start  = 6;
         filelock.l_len    = 7;

         if ( -1 == fcntl( fd, F_SETLKW, &filelock))
         {
            printf( "레코드 잠금에 실패해서 falinux를 쓰지 못했습니다.\n");
         }
         else
         {
            write( fd, "falinux", 7);
         }

         close( fd);
         printf( "자식: 프로그램을 종료합니다.\n");
         break;
      }
      default  :
      {
         printf( "부모: 파일을 열고 레코드 잠금하겠습니다.\n");
         fd = open( filename, O_RDWR, 0666);

         filelock.l_type   = F_RDLCK;
         filelock.l_whence = SEEK_SET;
         filelock.l_start  = 7;
         filelock.l_len    = 7;

         if ( -1 != fcntl( fd, F_SETLK, &filelock))
         {
            printf( "부모: 잠금에 성공했으며 3초간 대기합니다.\n");
            sleep( 3);
         }
         // 파일이 닫히면 자동으로 잠금이 해제
         close( fd);
      }
   }
}
]$ ./a.out
자식: 부모 프로세스를 위해 잠시 대기하겠습니다.
부모: 파일을 열고 레코드 잠금하겠습니다.
부모: 잠금에 성공했으며 3초간 대기합니다.
자식: 파일의 내용을 수정합니다.
]$ 자식: 프로그램을 종료합니다.

]$ cat test.txt
forumfalinux.COM HAPPY NEW YEAR!!
]$ 


'Linux > C/C++' 카테고리의 다른 글

dup() 파일 디스크립터 복사본 만들기  (0) 2012.07.11
strdup() 문자열의 clone 만들기  (0) 2012.07.11
ftruncate() 파일을 지정한 크기로 변경  (0) 2012.07.11
How to truncate a file in C?  (0) 2012.07.11
파일 유무 확인  (0) 2012.07.10
설명

파일을 지정한 크기로 변경합니다. 파일 크기를 변경하는 함수에는 2 가지가 있습니다.

  • truncate() : 파일 이름으로 파일 크기를 변경
  • ftruncate() : 파일 디스크립터로 파일 크기를 변경
헤더 unistd.h
형태 int ftruncate(int fildes, off_t length);
인수 int fildes 파일 디스크립터
  off_t length 파일 크기
반환 int

0 : 성공, -1: 실패

예제
// 예제에서는 파일의 크기를 100 byte로 변경합니다.
// 파일이 지정된 크기보다 작다면 나머지 채워지는 부분은 '\0'으로 채워지게 됩니다.

#include <stdio.h>         // puts()
#include <string.h>        // strlen()
#include <fcntl.h>         // O_WRONLY, O_CREAT
#include <unistd.h>        // write(), close(), ftruncate()

#define  BUFF_SIZE   1024

int main()
{
   int   fd;
   char *buff  = "forum.falinux.com";

   fd = open( "./test.txt", O_WRONLY ¦ O_CREAT, 0644);
   write(  fd, buff, strlen( buff));
   ftruncate( fd, 100);              // 파일 디스크립터로 파일 크기 조정

   close( fd);

   return 0;
}
]$ ./a.out
]$
파일을 헥사 에디터로 열어 보면 파일의 크기에 모자른 부분은 '\0'으로 채워진 것을 볼 수 있습니다.


'Linux > C/C++' 카테고리의 다른 글

strdup() 문자열의 clone 만들기  (0) 2012.07.11
fcntl() 레코드 잠금  (0) 2012.07.11
How to truncate a file in C?  (0) 2012.07.11
파일 유무 확인  (0) 2012.07.10
linux와 Windows의 파일 정보 접근  (0) 2012.07.10

I'm using C to write some data to a file. I want to erase the previous text written in the file in case it was longer than what I'm writing now. I want to decrease the size of file or truncate until the end. How can I do this?



If you want to preserve the previous contents of the file up to some length (a length bigger than zero, which the other answers provide), then POSIX provides the truncate() and ftruncate() functions for the job.

#include <unistd.h>
int ftruncate(int fildes, off_t length);
int truncate(const char *path, off_t length);

The name indicates the primary purpose - shortening a file. But if the specified length is longer than the previous length, the file grows (zero padding) to the new size. Note that ftruncate() works on a file descriptor, not a FILE *; you could use:

if (ftruncate(fileno(fp), new_length) != 0) ...error handling...

It is likely, though, that for your purposes, truncate on open is all you need, and for that, the options given by others will be sufficient.


'Linux > C/C++' 카테고리의 다른 글

fcntl() 레코드 잠금  (0) 2012.07.11
ftruncate() 파일을 지정한 크기로 변경  (0) 2012.07.11
파일 유무 확인  (0) 2012.07.10
linux와 Windows의 파일 정보 접근  (0) 2012.07.10
linux GetTickCount  (0) 2012.07.10
파일 유무 확인 Linux/C/C++ 2012. 7. 10. 15:52

1. fopen()의 리턴값을 확인한다.

FILE *fopen(const char *path, const char *mode);
FILE 포인터로 리턴을 하는데 open 실패시 NULL을 리턴하며, errno에 에러를 기록함.

2. access()로 확인한다.
int access(const char *pathname, int mode);
리턴값을 바로 확인하면 됨.

3. fstat()의 리턴값을 확인한다
int fstat(int filedes, struct stat *buf);
리턴값을 바로 확인하면 됨.

구차니즘을 털고 2008.01.06일 테스트
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <time.h>
int main()
{
        int ret = 0;
        clock_t before;
        clock_t after;
        double res;
        FILE *fp;
        struct stat st;

        before = clock();
        fp = fopen("test.txt","rb");
        after = clock();
        printf("%d - %d = %d tick\n",after, before,after - before);

        before = clock();
        ret = access("test2.txt",F_OK);
        after = clock();
        printf("%d - %d = %d tick\n",after, before,after - before);

        before = clock();
        ret = stat("test3.txt",&st);
        after = clock();
        printf("%d - %d = %d tick\n",after, before,after - before);

        return 0;
}

로 테스트 하는데, 전부 0 tick이 나온다 ㄱ-
결론은 어느걸 쓰던지 현존 시스템에서는 지장이 없다는 의미인데, 임베디드에서 쓰기 위해서는
조금 더 자세히 조사를 해봐야 할 듯 하다.

dmesg | grep CPU
CPU0: Intel(R) Pentium(R) 4 CPU 3.40GHz stepping 0a

$ time ./a.out
0 - 0 = 0 tick
0 - 0 = 0 tick
0 - 0 = 0 tick

real    0m0.001s
user    0m0.000s
sys     0m0.001s

이래저래 측정이 안되는건 마찬가지 ㄱ-


'Linux > C/C++' 카테고리의 다른 글

ftruncate() 파일을 지정한 크기로 변경  (0) 2012.07.11
How to truncate a file in C?  (0) 2012.07.11
linux와 Windows의 파일 정보 접근  (0) 2012.07.10
linux GetTickCount  (0) 2012.07.10
유닉스에서 디렉토리 다루기  (0) 2012.07.10

linux
int lstat(const char *restrict path, struct stat *restrict buf);
lstat() 함수의 두번째 인자로 넘어옴
lstat( 파일 풀경로, stst구조체 );

 struct stat {
    dev_t         st_dev;      /* device */
    ino_t         st_ino;      /* inode */
    mode_t        st_mode;     /* protection */
    nlink_t       st_nlink;    /* number of hard links */
    uid_t         st_uid;      /* user ID of owner */
    gid_t         st_gid;      /* group ID of owner */
    dev_t         st_rdev;     /* device type (if inode device) */
    off_t         st_size;     /* total size, in bytes */
    blksize_t     st_blksize;  /* blocksize for filesystem I/O */
    blkcnt_t      st_blocks;   /* number of blocks allocated */
    time_t        st_atime;    /* time of last access */
    time_t        st_mtime;    /* time of last modification */
    time_t        st_ctime;    /* time of last change */
};
atime : vi, cat, cd 등의 명령으로 접근
mtime : vi, >> 등으로 파일 수정 ( ls -l로 확인 가능시간)
ctime : chmod, chown 등과 관련 있음

Windows
HANDLE WINAPI FindFirstFile(
  __in          LPCTSTR lpFileName,
  __out         LPWIN32_FIND_DATA lpFindFileData
);
FindFirstFile() 함수의 두번째 인자로 넘어옴
FindFirstFile( 파일 경로, LPWIN32_FIND_DATA  구조체 )
 typedef struct _WIN32_FIND_DATAA {
    DWORD dwFileAttributes;
    FILETIME ftCreationTime;
    FILETIME ftLastAccessTime;
    FILETIME ftLastWriteTime;
    DWORD nFileSizeHigh;
    DWORD nFileSizeLow;
    DWORD dwReserved0;
    DWORD dwReserved1;
    CHAR   cFileName[ MAX_PATH ];
    CHAR   cAlternateFileName[ 14 ];
#ifdef _MAC
    DWORD dwFileType;
    DWORD dwCreatorType;
    WORD  wFinderFlags;
#endif
} WIN32_FIND_DATAA, *PWIN32_FIND_DATAA, *LPWIN32_FIND_DATAA;
ftCreationTime : 파일 생성시간
ftLastAccessTime : 파일 접근 시간
ftLastWriteTime : 파일 수정 시간

기준시간
Windows
 1601년 1월 1일, 64비트 정수, 100ns에 1증가
linux    
 1970년 1월 1일, 32비트 정수, 1초에 1증가

Windows -> linux : windowsTime / 10000000 - 11644473600
linux -> Windows : (linuxTime + 11644473600) * 10000000



MSDN를 참고 하자.
http://support.microsoft.com/kb/q167296/
#define Int32x32To64(a, b)  ((__int64)(((__int64)((long)(a))) * ((long)(b))))


'Linux > C/C++' 카테고리의 다른 글

How to truncate a file in C?  (0) 2012.07.11
파일 유무 확인  (0) 2012.07.10
linux GetTickCount  (0) 2012.07.10
유닉스에서 디렉토리 다루기  (0) 2012.07.10
Configuration module to update in a file  (0) 2012.07.02