月: 2012年12月

未分類

パノラマ雲台製作中

IBL 素材作成のためにパノラマ機材を購入してパノラマ野郎化した勢いで自動雲台まで作りたくなって、ここ数カ月、細々と設計やら部品調達やらを進めていたモロモロが少しづつ形になってきたのでお披露目。一番のネックだったフレーム部分ができて仮組みしてみました。

制御制御部分は Raspberry Pi を使って、パノラマ撮影+多段撮影による HDR パノラマ撮影まで対応する予定です。サーボとカメラを制御するためのソフトウェア側も大体メドはついているので、あとはパッケージングをどうするかというところです。

Programming

ネットワークファイルへの stat() は、嘘をつかれることがあるよ!!

ネットワークファイル周りは結構地雷がたくさんあって、これも今年私がドハマりしたものです。
私が確認しているのはWindows7+Windows Server の組み合わせのみで XP などでは起きない症状ですが、ネットワーク上にファイルを作成/削除してもどこかでキャッシュが聞いているようで、数秒間はstat() が嘘情報を返すというものがあります。


import os
import time
import sys
import ctypes


def CreateHardLink(src, dest):
    if os.path.exists(dest):
        print 'file exist'
        raise FileExistError

    CreateHardLinkW = ctypes.windll.kernel32.CreateHardLinkW
    CreateHardLinkW.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_int]

    ret = CreateHardLinkW(dest, src, 0)
    if ret ==0:
        print ctypes.FormatError()
        print src
        print dest

        raise PlatformError

def clearData():
    for i in range(10):
        f = os.path.join(d, r'%s\%03d.txt' % (d,i))
        if os.path.exists(f):
            os.remove(f)

def testCreate(path):
    s = os.path.join(path, r'src.txt')
    open(s, 'w').close()

    for i in range(10):
        print ('create %d' % (i))

        f = os.path.join(path, r'%s\%03d.txt' % (path,i))

        CreateHardLink(s, f)

        count = 0
        while not os.path.exists(f):
            print ('file not found %d %d' % (i, count))
            time.sleep(0.1)
            count += 1

testCreate(sys.argv[1])
C:\temp>python linkPerformanceTest.py \\Nas01\PJ\tmp
create 0
file not found 0 0
file not found 0 1
file not found 0 2
file not found 0 3
file not found 0 4
file not found 0 5
file not found 0 6

(以下略)

これで不思議なのは、Explorer ではきちんとファイルがあると認識されていることなんですね。どうやったら正確な情報を得られるのか。。。謎です。

※。。。と書いている途中で、もしかしたらAPI 経由で取ればいけるのかも?と思えてきました。後から追試してみます。

この症状はネットを漁ってみると Windos に限って起こるわけではなく、NFS ベースの Linux システムでもオプションによっては起こるようです。ただ、各種対応策を取っても解決できなかったのでちょっと特殊な理由なのかもしれません。

こういうの、地味に困るんですよね、、、ある程度時間が経ってから確認するときちんと動くのに、ちょっとシビアなタイミング(テスト環境では4.5秒以内)でアクセスすると発動する地雷なのです。トホホホホ。。。。。

Programming

ネットワーク上のファイルのハードリンクのカウントは正確に取得できないことがあるみたいだよ!!

NTFSで一つのファイルに張れるハードリンクは1023個までだよ!! の続き記事です。

1023 個って数字な時点で SAN 値ダダ下がり、もう一歩でソウルジェム真っ黒!!ってところまで来てたものの、自分の用途ならば限界超えてリンクを張るなら一つファイルをコピーしてハードリンク先を半分そっちに割り振ればいいやと思っていたので、そのための検証をしていたらここでもドハマりしたのでこれ以上不幸な(ry

UN*X の場合、ファイルにハードリンクされた数は stat で取得できます。例えば、Python のドキュメントだとこのあたり。ところが、こいつは Windows では正しく動きません。 OKOK。これは想定内。これくらいで動じてたらソウルジェムが幾つあっても足りない。

Windows にはこれらの情報を得るための API 、GetFileInformationByHandle() があります。ハードリンク数を取得するのはこちらが正解。これを使ってリンク数を取得するテストプログラムは以下のとおり

import os
import sys
import stat
import ctypes

import win32file

def CreateHardLink(src, dest):
    CreateHardLinkW = ctypes.windll.kernel32.CreateHardLinkW
    CreateHardLinkW.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_int]

    ret = CreateHardLinkW(dest, src, 0)
    if ret ==0:
        print ctypes.FormatError()
        print src
        print dest

        raise

    return ret

def getNumHardLink(fileName):
    hFile = win32file.CreateFile (
        fileName,
        win32file.GENERIC_READ,
        win32file.FILE_SHARE_READ,
        None,
        win32file.OPEN_EXISTING,
        0,
        None
        )

    (attributes,
     created_at, accessed_at, written_at,
     volume,
     file_hi, file_lo,
     n_links,
     index_hi, index_lo
     ) = win32file.GetFileInformationByHandle (hFile)

    hFile.Close()

    return n_links

d = sys.argv[1]
print d
src = os.path.join(d, 'src.txt')
dest = os.path.join(d, 'dest.txt')

if not os.path.exists(src):
    open(src, 'w').close()

CreateHardLink(src, dest)

s = os.stat(src)
if s.st_nlink == 2:
    print 'OK by stat'
else:
    print ('invalid link count by stat : %d' % s.st_nlink)

n_links = getNumHardLink(src)
if n_links == 2:
    print 'OK by API'
else:
    print ('invalid link count by API : %d' % n_links)

比較のために stat でも情報を拾っています。

C:\temp>python countLink.py c:\temp
c:\temp
invalid link count by stat : 0
OK by API

ちゃんと動いています。

だがしかし!!ここで一筋縄でいかないのが Windows 様。ネットワーク上のファイルシステムを指定してみると。。。

C:\temp>python countLink.py \\nas01\PJ\test
\\nas01\PJ\test
invalid link count by stat : 0
invalid link count by API : 1

え。。。。

え。。。。。

はい、ちゃんと値が取れません。ちなみに、これもサーバーによるらしく某所ではこの症状が起きてドハマりしました。
ちなみに Windows7+QNAP(Linux) ではきちんと動いているっぽいです。

たまたま外れのサーバーだったのか、それとも Windows ベースのものだったからなのか。。。どなたか他の環境で検証していただけないでしょうか。
この数字が取れないと本当に困るんですけど。。。。

これ以外でハードリンクされているかどうかを確認するには、先ほどの GetFileInformationByHandle() でファイルの ID が取れるので、
それを比較するしかなさそうな予感がします。。。。

Programming

NTFSで一つのファイルに張れるハードリンクは1023個までだよ!!

こんなところにこんな罠があるとは夢にも思わず、ガッツリやられたのでこれ以上不幸な人を生み出さないためにメモ。

Windows の NTFS で作るハードリンクは、一つのファイルに対して 1023 個が MAX のようです(!!)
恐ろしいことに、これ、日本語で書かれたドキュメントが見つからないんですね、、、みんなそこまで使ってないんでしょうか?

一番詳しく書かれているっぽいのが英語の Wikipedia。

Hard link

The maximum number of hard links to a single file is limited by the size of the reference counter: with NTFS this is limited to 1023 because a 10 bit field is used for this purpose.

NTFSでは、ハードリンクを管理するための領域として 10bit しか確保してないから 1023 個しかリンクできないよ♪ってことらしいです。おおう、じーざす!!

本当かどうか、テストしてみました。

import os
import ctypes

def CreateHardLink(src, dest):
    CreateHardLinkW = ctypes.windll.kernel32.CreateHardLinkW
    CreateHardLinkW.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_int]

    ret = CreateHardLinkW(dest, src, 0)
    if ret ==0:
        print ctypes.FormatError()
        print src
        print dest

        raise

    return ret

src = r'c:\temp\src.txt'
if not os.path.exists(src):
    open(src, 'w').close()

for i in range(2048):
    print i
    CreateHardLink(src, os.path.join(r'c:\temp', '%04d.txt' % i))

C:\temp>python testLink.py
0
1
2
3
4
5
6
7
8
9
10

(中略)

1020
1021
1022
1023
ファイル システムでサポートされている以上の数のリンクをファイルに作成しようとしました。
c:\temp\src.txt
c:\temp\1023.txt
Traceback (most recent call last):
File “testLink.py”, line 24, in
CreateHardLink(src, os.path.join(r’c:\temp’, ‘%04d.txt’ % i))
File “testLink.py”, line 14, in CreateHardLink
raise
TypeError: exceptions must be old-style classes or derived from BaseException, not NoneType

C:\temp>

本当でした。。。

ちなみに、この制限は NTFS によるものなので Linux ベースのファイルサーバーに対して同様なことをしてもエラーにならないみたいです。
私の環境(Windows7+QNAP)では、2048 個のリンクを張ることもできました。

Windows でハードリンクを張るっていうことがかなり少ないのでハマることはあまりないのかもしれませんが、こういうことが想定されるシステムではちゃんとした OS で動いているファイルサーバーを用意した方がいいかもしれません。