PHP에서의 파일 락에 대해서..

lsj0713의 이미지

예전에는 php에서 파일을 열고 닫을때 그냥 별 생각없이 fopen, fclose를 했는데, 여기저기서 주워들은 바에 의하면 여러명이 동시에 해당 페이지에 접근할 때 문제가 생기는 것 같더군요. 그래서 다음과 같은 파일 락 클래스를 만들었습니다.

파일에 락을 거는 방법은 이렇습니다.

1. filename.lock 파일을 생성
2. filename.lock 파일을 읽기 모드로 연다.
3. filename.lock에 flock 함수로 락을 건다. 읽기 락이면 LOCK_SH로, 쓰기 락이면 LOCK_EX로 락을 건다.

FileLock.php :

<?php

class FileLock
{
    var $errNo;
    var $errMsg;

    var $fileName;
    var $lockType;

    var $lockFileName_;
    var $fp_;

function FileLock($filename, $type = "r")
{
    $this->errNo = 0;
    $this->errMsg = "";

    // filename이 적법한지 검사
    if ( !is_file($filename) )
    {
        $this->setErr(1, "'$filename' is not a file");
        return;
    }

    // type이 r, R, w, R 중 하나인지 검사
    if ( !in_array($type, array("r", "R", "w", "W")) )
    {
        $this->setErr(2, "'$type' is a invalid lock type");
        return;
    }

    // 맴버 변수 초기화
    $this->fileName = $filename;
    $this->lockType = $type;
    $this->lockFileName_ = $filename . ".lock";

    // lock 파일 생성
    @touch($this->lockFileName_);
    // lock 파일 열기
    $this->fp_ = fopen($this->lockFileName_, 'r');

    // 파일을 여는데 실패했을 경우
    if ( !$this->fp_ )
    {
        $this->setErr(3, "Can't open file '$this->lockFileName_'");
        return;
    }

    // 소멸자 등록. 여기에 위치한 이유는 파일이 열리지 않으면 소멸자를
    // 굳이 부를 필요가 없기 때문이다.
    register_shutdown_function(array(&$this, '_FileLock'));

    // lock 파일에 lock을 건다.
    switch ( $this->lockType )
    {
        case 'r': case 'R': flock($this->fp_, LOCK_SH); break;
        case 'w': case 'W': flock($this->fp_, LOCK_EX); break;
    }

    return;
}

// 소멸자
function _FileLock() { $this->unlock(); }

function unlock()
{
    if ( is_resource($this->fp_) )
    {
        flock($this->fp_, LOCK_UN);
        fclose($this->fp_);
    }
}

function setErr($errNo, $errMsg)
{
    $this->errNo = $errNo;
    $this->errMsg = $errMsg;
}

};

?>

그리고 아래는 실제 사용 예입니다.

FileLock.Test.php :

<?php

include("FileLock.php");

$filename = "test1.txt";

$fl1 = new FileLock($filename);
$fp1 = fopen($filename, "r");

echo fgets($fp1, 4096);

fclose($fp1);
$fl1->unlock();

?>

그런데 문제는, 이게 올바른 방식으로 짠 코드인지, 혹은 제대로 동작하는지 확인할 방법이 마땅히 떠오르질 않더군요;;; 일단 인터넷에서 다른 소스들을 둘러봐도 대충 비슷한 방식으로 lock을 거는 듯하지만 확실하지는 않고, 그렇다고 확인을 하려면 여러명이 동시에 접근하는 환경에서 해야 하는데, 어떻게 해야 할지도 모르겠고...

이거 완전 밥떠먹여 달라는 질문인 듯 하지만 마땅한 해결책이 떠오르질 않아서 이렇게 질문을 올립니다. 위의 클래스에 뭔가 문제는 없을까요?

ps. 궁금한 부분은 크게 두가지입니다.

1. PHP의 fopen과 fclose 함수에서 자체적으로 file lock을 지원하는가?

그렇다면 위의 클래스는 삽질이 되겠죠;;; soojung 이라던가 기타 PHP 소스들을 봐도 그냥 fopen하고 fclose하는 경우가 많았지, 파일 락을 쓰는 경우는 거의 없었던걸로 기억합니다.

2. 만약 1번이 아니라면, 위의 코드는 여러 프로세스에서 동시에 같은 파일에 접근할 때 생기는 문제를 막아줄 수 있는가?

사실 파일 락은 그동안 잘 써보지를 않아서...;;; php.net의 flock 함수에 달린 리플들을 읽고 대충 비슷하게 만들긴 했는데 제대로 만들었는지는 자신이 없군요;;;

ssehoony의 이미지

테스트 프로그램을 하나 만들어서 테스트 하세요.

파일을 락을 건 상태에서 동일 파일을 다시 한번 열어보세요.
그러면 쉽게 테스트가 가능하겠네요.

익명 사용자의 이미지

$f = fopen();
flock($f, LOCK_EX);
~~~~~~
flock($f, LOCK_UN);
flose($f);

위 클래스가 굳이 만든다면 /var/run 밑에 보이는 것 처럼
사용하려고 한 것으로 보이는데 따로 노는 군요.
<?php
include("FileLock.php");
$filename = "test1.txt";
$fl1 = new FileLock($filename);
$fl1->get();
$fl1->close();
?>
저 같으면 이런 스타일로....

register_shutdown_function(array(&$this, '_FileLock'));
했다면 $fl1->unlock() 을 안 부르려고 한 것 아닌가요?
이렇게 안 하는 것이 좋을 것 같습니다.

lsj0713의 이미지

devilhero wrote:

테스트 프로그램을 하나 만들어서 테스트 하세요.

파일을 락을 건 상태에서 동일 파일을 다시 한번 열어보세요.
그러면 쉽게 테스트가 가능하겠네요.

제가 확인하고 싶은 것은 단순히 위의 클래스의 동작 여부가 아니라(flock 함수에 껍데기만 씌웠으니 당연히 잘 작동하겠죠;;;), 여러 사람이 동시에 접근했을 때 문제가 생기지 않을까 하는 겁니다. 총돌 문제를 제대로 해결해 주는가, 혹은 성능상의 불이익을 가져오지 않는가 등등... 이건 그냥 간단한 예제파일로는 확인하기 어렵죠;;

Quote:

register_shutdown_function(array(&$this, '_FileLock'));
했다면 $fl1->unlock() 을 안 부르려고 한 것 아닌가요?
이렇게 안 하는 것이 좋을 것 같습니다.

아닙니다. 사용자가 F5 키를 막 누르거나 중간에 연결이 끊겨서 작업이 중단되었을 때도 파일 락을 해제하도록 하기 위함입니다.

익명 사용자의 이미지

Quote:

Quote:

register_shutdown_function(array(&$this, '_FileLock'));
했다면 $fl1->unlock() 을 안 부르려고 한 것 아닌가요?
이렇게 안 하는 것이 좋을 것 같습니다.

아닙니다. 사용자가 F5 키를 막 누르거나 중간에 연결이 끊겨서 작업이 중단되었을 때도 파일 락을 해제하도록 하기 위함입니다.


그렇다면 user_ignore_abort() 를 사용하겠습니다.

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.