select () не ждет никаких изменений - PullRequest
1 голос
/ 11 августа 2011

Я не совсем уверен, что я делаю неправильно. Мне нужно получить событие изменения файла системным вызовом select (). Так что проблема в том, что он не ждет и сразу возвращает ИСТИННЫЙ результат. Похоже, я не изменил положение файла до конца файла, что я и сделал.
Вот код

(defvar *fd-setsize* 1024)

(defvar *fd-bits-size* (/ *fd-setsize* sb-vm:n-machine-word-bits))

(defcstruct ccx-fd-set
  (fds-bits :long :count #.*fd-bits-size*))

(defun ccx-strerror (errnum)
  "Wrapper for strerror - return string describing error number"
  (foreign-funcall "strerror" :int errnum :string))

(defun ccx-test (function-result &optional (caller nil caller-p))
  "Wrapper for native functuions. It on succecc it will return function-result, but if not,
   it will also report an error from errno."
  (labels ((error-signal ()
         (if caller-p 
         (warn "~a ~a" caller (ccx-strerror *errno*))
         (warn "~a" (ccx-strerror *errno*)))))
    (cond
      ((and (realp function-result)
        (minusp function-result)) (error-signal)))
    function-result))

(defun ccx-select (nfds readfds writefds exceptfds timeout)
  (ccx-test (foreign-funcall "select" :int nfds :pointer readfds :pointer writefds
                 :pointer exceptfds :pointer timeout :int) "ccx-select"))

(defun ccx-pselect (nfds readfds writefds exceptfds timeout sigmask)
  (ccx-test (foreign-funcall "pselect" :int nfds :pointer readfds :pointer writefds
                 :pointer exceptfds :pointer timeout :pointer sigmask :int) "ccx-pselect"))

;; #define __FD_SET(d, set) \
;;   ((void) (__FDS_BITS (set)[__FDELT (d)] |= __FDMASK (d)))
(defmacro fd-set (offset fd-set)
  (with-gensyms (word bit)
    `(with-foreign-slots ((fds-bits) ,fd-set ccx-fd-set)
       (multiple-value-bind (,word ,bit) (floor ,offset *fd-bits-size*)
     (setf (mem-aref fds-bits :long ,word)
           ;; #define  __FDMASK(d) ((__fd_mask) 1 << ((d) % __NFDBITS))
           (logior (the (unsigned-byte #.*fd-bits-size*)
             (ash 1 ,bit))
               ;; #define  __FDELT(d)  ((d) / __NFDBITS)
               (mem-aref fds-bits :long ,word)
               ))))))

;; #define __FD_CLR(d, set) \
;;   ((void) (__FDS_BITS (set)[__FDELT (d)] &= ~__FDMASK (d)))
(defmacro fd-clr (offset fd-set)
  (with-gensyms (word bit)
    `(with-foreign-slots ((fds-bits) ,fd-set ccx-fd-set)
       (multiple-value-bind (,word ,bit) (floor ,offset sb-vm:n-machine-word-bits)
     (setf (mem-aref fds-bits :long ,word)
           (logand (the (unsigned-byte #.*fd-bits-size*)
             (ash 1 ,bit))
               (mem-ref fds-bits :long ,word)))))))

;; #define __FD_ISSET(d, set) \
;;   ((__FDS_BITS (set)[__FDELT (d)] & __FDMASK (d)) != 0)
(defmacro fd-isset (offset fd-set)
  (with-gensyms (word bit)
    `(with-foreign-slots ((fds-bits) ,fd-set ccx-fd-set)
       (multiple-value-bind (,word ,bit) (floor ,offset *fd-bits-size*)
     ;; (logbitp ,bit (mem-aref fds-bits :long ,word))
     (logbitp ,bit (mem-aref fds-bits :long ,word))))))

;; # define __FD_ZERO(set)  \
;;   do {                                 \
;;     unsigned int __i;                           \
;;     fd_set *__arr = (set);                      \
;;              4 * 32 = 128  /  4 
;;     for (__i = 0; __i < sizeof (fd_set) / sizeof (__fd_mask); ++__i)       \
;;       __FDS_BITS (__arr)[__i] = 0;                    \
;;   } while (0)
(defmacro fd-zero (&rest fd-sets)
  `(progn
     ,@(loop for el in fd-sets
      collect `(with-foreign-slots ((fds-bits) ,el ccx-fd-set)
             ,@(loop for index upfrom 0 below *fd-bits-size*
              collect `(setf (mem-aref fds-bits :long ,index) 0))))))


(defmacro fd-set-print (&rest fd-sets)
  `(progn
     ,@(loop for el in fd-sets
      collect `(progn
             (format t "~&~s: " ',el)
             ,@(loop for index upfrom 0 below *fd-bits-size* collect
                    `(format t "~d " (mem-aref (foreign-slot-value ,el 'ccx-fd-set 'fds-bits) :long ,index))
                  finally (format t "~%"))))))

Это мой скрипт запуска

(with-open-file (fd "/tmp/test.txt")
  (with-foreign-objects ((fd-set-r 'ccx-fd-set)
             (fd-set-w 'ccx-fd-set)
             (fd-set-x 'ccx-fd-set)
             (timeout 'ccx-timespec))

    (file-position fd (file-length fd))

    (setf (foreign-slot-value timeout 'ccx-timespec 'ccx-tv-sec)  10)
    (setf (foreign-slot-value timeout 'ccx-timespec 'ccx-tv-nsec)  0)

    ;; PRINTING && FLUSHING && PRINTING
    (fd-set-print  fd-set-r fd-set-w fd-set-x)
    (fd-zero fd-set-r fd-set-w fd-set-x)
    (fd-set-print  fd-set-r fd-set-w fd-set-x)

    (fd-set (sb-sys:fd-stream-fd fd) fd-set-r)
    (fd-set (sb-sys:fd-stream-fd fd) fd-set-w)
    (fd-set (sb-sys:fd-stream-fd fd) fd-set-x)
    (fd-set-print  fd-set-r fd-set-w fd-set-x)

    (let ((res (ccx-select *fd-setsize* fd-set-r fd-set-w (null-pointer) timeout)))
      (if (< res 0)
      (format t "~& select () error")
      (loop for index upfrom 0 below *fd-setsize* do
           (progn
         (when (fd-isset index fd-set-r)
           (format t "~& fd ~d read" index))
         (when (fd-isset index fd-set-w)
           (format t "~& fd ~d write" index))
         ;; (when (fd-isset index fd-set-x)
         ;;   (format t "~& fd ~d ex" index))
         ))))))

Так вот мой пример C, который не работает для меня тоже.

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <fcntl.h>
#include <stdio.h>

int main ()
{
  FILE * pFile;
  long size;

  pFile = fopen ("/tmp/test.txt","rb");
  if (pFile==NULL) perror ("Error opening file");
  else
    {
      while (1){
    fseek (pFile, 0, SEEK_END);
    size=ftell (pFile);
    printf ("Size of myfile.txt: %ld bytes.\n",size);

    fd_set fds;
    struct timeval tv;


    int fd = fileno(pFile);
    FD_ZERO(&fds);
    FD_SET(fd,&fds);
    tv.tv_sec = 2;
    tv.tv_usec = 0;
    int rc = select(fd+1, &fds, NULL, NULL, &tv);

    if (rc < 0) {
      printf("failed\n");
      /* continue; */
    } else if (rc > 0 && FD_ISSET(fd,&fds)) {
      printf("read\n");
    } else  {
      printf("timeout\n");
      /* continue; */
    }

      }
      fclose (pFile);
    }
  return 0;
}

Результат этой программы:

Size of test.txt: 2267 bytes.
read
Size of test.txt: 2267 bytes.
read

Что не так с моим кодом?

Ответы [ 2 ]

4 голосов
/ 12 августа 2011

Вы не делаете ничего плохого

select () / poll () и т.п. не работает для обычных файлов.

"Файловые дескрипторы, связанные с обычными файлами, всегда выбирают true для готовности к чтению, готовности к записи и условий ошибки." - с здесь

Если вы хотите отслеживать файлы на предмет изменений, вы можете использовать gamin или API-интерфейсы, зависящие от операционной системы, такие как inotify в Linux, kqueue в BSD .

1 голос
/ 12 августа 2011

Обычные (дисковые) файлы всегда готовы к чтению;select() действительно полезно только на устройствах, таких как tty и сокеты.

POSIX говорит:

Дескриптор считается готовым к чтению, когда вызовдля функции ввода с O_NONBLOCK очистка не будет блокировать, будет ли функция успешно передавать данные.(Функция может возвращать данные, указание конца файла или ошибку, отличную от той, которая указывает на то, что она заблокирована, и в каждом из этих случаев дескриптор считается готовым к чтению.)

Aдескриптор считается готовым к записи, когда вызов функции вывода с O_NONBLOCK clear не блокирует, будет ли функция успешно передавать данные.

Плюс соответствующая кавычка также в ответе nos :

Файловые дескрипторы, связанные с обычными файлами, должны всегда выбирать true для условий готовности к чтению, готовности к записи и ошибок.

...