Doge log

Abby CTO 雑賀 力王のオフィシャルサイトです

プロセス間ファイル記述子の受け渡し

こんにちわ!DQNです!
thundering herd 問題の解決法のひとつ、子プロセスにファイル記述子を送りつけるって奴を実験。
Python C拡張のサンプルにもなってるけどDQNなので難しいことはまったくできませんよ!

sendmsg.c
#include <Python.h>

#include <sys/socket.h>

static PyObject *
sendmsg_sendmsg(PyObject *self, PyObject *args)
{
    int conn, fd, res;
    char dummy;
    char buf[CMSG_SPACE(sizeof(int))];
    struct msghdr msg = {0};
    struct iovec iov;
    struct cmsghdr *cmsg;

    if(!PyArg_ParseTuple(args, "ii", &conn, &fd))
        return NULL;

	iov.iov_base = &dummy;
	iov.iov_len = 1;
	msg.msg_control = buf;
	msg.msg_controllen = sizeof(buf);
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	cmsg = CMSG_FIRSTHDR(&msg);
	cmsg->cmsg_level = SOL_SOCKET;
	cmsg->cmsg_type = SCM_RIGHTS;
	cmsg->cmsg_len = CMSG_LEN(sizeof(int));
	msg.msg_controllen = cmsg->cmsg_len;
	*(int*)CMSG_DATA(cmsg) = fd;

	Py_BEGIN_ALLOW_THREADS
	res = sendmsg(conn, &msg, 0);
	Py_END_ALLOW_THREADS
        
	if (res < 0)
		return PyErr_SetFromErrno(PyExc_OSError);
	Py_RETURN_NONE;

}

static PyObject *
sendmsg_recvmsg(PyObject *self, PyObject *args)
{
	int conn, fd, res;
	char dummy;
	char buf[CMSG_SPACE(sizeof(int))];
	struct msghdr msg = {0};
	struct iovec iov;
	struct cmsghdr *cmsg;

	if (!PyArg_ParseTuple(args, "i", &conn))
		return NULL;

	iov.iov_base = &dummy;
	iov.iov_len = 1;
	msg.msg_control = buf;
	msg.msg_controllen = sizeof(buf);
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	cmsg = CMSG_FIRSTHDR(&msg);
	cmsg->cmsg_level = SOL_SOCKET;
	cmsg->cmsg_type = SCM_RIGHTS;
	cmsg->cmsg_len = CMSG_LEN(sizeof(int));
	msg.msg_controllen = cmsg->cmsg_len;

	Py_BEGIN_ALLOW_THREADS
	res = recvmsg(conn, &msg, 0);
	Py_END_ALLOW_THREADS

	if (res < 0)
		return PyErr_SetFromErrno(PyExc_OSError);

	fd = *(int*)CMSG_DATA(cmsg);
	return Py_BuildValue("i", fd);
}


static PyMethodDef SendmsgMethods[] = { 
    {"sendmsg", sendmsg_sendmsg, METH_VARARGS, ""}, 
    {"recvmsg", sendmsg_recvmsg, METH_VARARGS, ""}, 
    {NULL, NULL, 0, NULL} /* Sentinel */ 
}; 


PyMODINIT_FUNC 
init_sendmsg(void) 
{ 
    PyObject *m; 
    m = Py_InitModule("_sendmsg", SendmsgMethods); 
}

そのまんま。だってDQNだし。
DQNなのでビルドもsetup.py書いてまかせます。

setup.py
from distutils.core import setup, Extension

module1 = Extension('_sendmsg', sources = ['sendmsg.c'])

setup(
        name='sendmsg',
        version='0.1',
        description='This is a DQN package', 
        ext_modules = [module1]) 

python setup.py build
sudo python setup.py install

とかでビルド、インスコDQNなので楽な方法しかできませんよ!
動かしてみようかな、だってDQNだし。

test.py
import _sendmsg
import os
import socket
import time

class Test(object):

    def __init__(self):
        self.is_child = None

    def fork(self):
        pid = os.fork()

        if pid > 0:
            self.is_child = 0
        else:
            self.is_child = 1
    
    def ready(self):
        if self.is_child:
            self._child_ready()
        else:
            self._parent_ready()

    def _parent_ready(self):
        file = open('test.txt', 'r')
        fd = file.fileno()
        print "parent %s" % os.fstat(fd)
        time.sleep(2)
        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        sock.connect('test.sock')
        _sendmsg.sendmsg(sock.fileno(), fd)

    def _child_ready(self):
        sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        sock.bind('test.sock')
        sock.listen(1)
        conn, addr = sock.accept()
        fd = _sendmsg.recvmsg(conn.fileno())
        print "child  %s" % os.fstat(fd)
        file = os.fdopen(fd, 'r')
        print file.readline()


if __name__ == '__main__':
    t = Test()
    t.fork()
    t.ready()

なんかforkとか久々に使った気もする。
あってるのかわかんないな、このコード。だってDQNだし。

parent (33188, 24590563L, 234881026L, 1, 501, 20, 23L, 1221661223, 1221660036, 1221660036)
child (33188, 24590563L, 234881026L, 1, 501, 20, 23L, 1221661223, 1221660036, 1221660036)

statの結果は同じっぽい。
でもファイルエントリテーブルが共有されるはずだけどカレントファイルポジションの位置がおかしかった。
送信する前にseekとかでちょっとでもずらすと受けとる側ではカレントファイルポジションが末尾になってた。
fdを渡たして読むとまた違うのかも知れない。
よーわからんな、だってDQNだから。

Python C拡張、IPCなど以下の2冊がとても参考になります。

Pythonクィックリファレンス

Pythonクィックリファレンス

詳解UNIXプログラミング

詳解UNIXプログラミング

  • 作者: W.リチャードスティーヴンス,W.Richard Stevens,大木敦雄
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2000/12
  • メディア: 単行本
  • 購入: 8人 クリック: 103回
  • この商品を含むブログ (40件) を見る