程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> Linux 新的API signalfd、timerfd、eventfd使用說明

Linux 新的API signalfd、timerfd、eventfd使用說明

編輯:關於PHP編程

Linux 新的API signalfd、timerfd、eventfd使用說明


Linux 新的API signalfd、timerfd、eventfd使用說明

三種新的fd加入linux內核的的版本:

signalfd:2.6.22

timerfd:2.6.25

eventfd:2.6.22

三種fd的意義:

lsignalfd

傳統的處理信號的方式是注冊信號處理函數;由於信號是異步發生的,要解決數據的並發訪問,可重入問題。signalfd可以將信號抽象為一個文件描述符,當有信號發生時可以對其read,這樣可以將信號的監聽放到select、poll、epoll等監聽隊列中。

ltimerfd

可以實現定時器的功能,將定時器抽象為文件描述符,當定時器到期時可以對其read,這樣也可以放到監聽隊列的主循環中。

leventfd

實現了線程之間事件通知的方式,也可以用於用戶態和內核通信。eventfd的緩沖區大小是sizeof(uint64_t);向其write可以遞增這個計數器,read操作可以讀取,並進行清零;eventfd也可以放到監聽隊列中,當計數器不是0時,有可讀事件發生,可以進行讀取。

三種新的fd都可以進行監聽,當有事件觸發時,有可讀事件發生。

signalfd涉及API:

  1. #include
  2. int signalfd(int fd, const sigset_t *mask, int flags);

參數fd:如果是-1則表示新建一個,如果是一個已經存在的則表示修改signalfd所關聯的信號;

參數mask:信號集合;

參數flag:內核版本2.6.27以後支持SFD_NONBLOCK、SFD_CLOEXEC;

成功返回文件描述符,返回的fd支持以下操作:read、select(poll、epoll)、close

l例子


  1. #include
  2. #include
  3. #include
  4. #include
  5. #include

  6. #define handle_error(msg) \
  7. do { perror(msg); exit(EXIT_FAILURE); } while (0)

  8. int main(int argc, char *argv[])
  9. {
  10. sigset_t mask;
  11. int sfd;
  12. struct signalfd_siginfo fdsi;
  13. ssize_t s;

  14. sigemptyset(&mask);
  15. sigaddset(&mask, SIGINT);
  16. sigaddset(&mask, SIGQUIT);

  17. if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
  18. handle_error("sigprocmask");

  19. sfd = signalfd(-1, &mask, 0);
  20. if (sfd == -1)
  21. handle_error("signalfd");

  22. for (;;) {
  23. s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo));
  24. if (s != sizeof(struct signalfd_siginfo))
  25. handle_error("read");

  26. if (fdsi.ssi_signo == SIGINT) {
  27. printf("Got SIGINT\n");
  28. } else if (fdsi.ssi_signo == SIGQUIT) {
  29. printf("Got SIGQUIT\n");
  30. exit(EXIT_SUCCESS);
  31. } else {
  32. printf("Read unexpected signal\n");
  33. }
  34. }
  35. }

L17-L21:將感興趣的信號加入到sigset_t中;

L24:調用signalfd,把信號集與fd關聯起來,第一個參數為-1表示新建一個signalfd,不是-1並且是一個合法的signalfd表示向其添加新的信號。

L29:阻塞等待信號的發生並讀取。根據讀取的結果可以知道發生了什麼信號。

timerfd涉及的API

  1. #include
  2. int timerfd_create(int clockid, int flags);
  3. int timerfd_settime(int fd, int flags, const struct itimerspec *new_value,struct itimerspec *old_value);
  4. int timerfd_gettime(int fd, struct itimerspec *curr_value);

timerfd_create:創建一個timerfd;返回的fd可以進行如下操作:read、select(poll、epoll)、close

timerfd_settime:設置timer的周期,以及起始間隔

timerfd_gettime:獲取到期時間。

  1. //函數參數中數據結構如下:
  2. struct timespec
  3. {
  4. time_t tv_sec; /* Seconds */
  5. long tv_nsec; /* Nanoseconds */
  6. };
  7. struct itimerspec
  8. {
  9. struct timespec it_interval; /* Interval for periodic timer */
  10. struct timespec it_value; /* Initial expiration */
  11. };

l例子


  1. #include
  2. #include
  3. #include
  4. #include
  5. #include
  6. #include
  7. #include /* Definition of uint64_t */

  8. #define handle_error(msg) \
  9. do { perror(msg); exit(EXIT_FAILURE); } while (0)

  10. void printTime()
  11. {
  12. struct timeval tv;
  13. gettimeofday(&tv, NULL);
  14. printf("printTime: current time:%ld.%ld ", tv.tv_sec, tv.tv_usec);
  15. }

  16. int main(int argc, char *argv[])
  17. {
  18. struct timespec now;
  19. if (clock_gettime(CLOCK_REALTIME, &now) == -1)
  20. handle_error("clock_gettime");

  21. struct itimerspec new_value;
  22. new_value.it_value.tv_sec = now.tv_sec + atoi(argv[1]);
  23. new_value.it_value.tv_nsec = now.tv_nsec;
  24. new_value.it_interval.tv_sec = atoi(argv[2]);
  25. new_value.it_interval.tv_nsec = 0;

  26. int fd = timerfd_create(CLOCK_REALTIME, 0);
  27. if (fd == -1)
  28. handle_error("timerfd_create");

  29. if (timerfd_settime(fd, TFD_TIMER_ABSTIME, &new_value, NULL) == -1)
  30. handle_error("timerfd_settime");

  31. printTime();
  32. printf("timer started\n");

  33. for (uint64_t tot_exp = 0; tot_exp < atoi(argv[3]);)
  34. {
  35. uint64_t exp;
  36. ssize_t s = read(fd, &exp, sizeof(uint64_t));
  37. if (s != sizeof(uint64_t))
  38. handle_error("read");

  39. tot_exp += exp;
  40. printTime();
  41. printf("read: %llu; total=%llu\n",exp, tot_exp);
  42. }

  43. exit(EXIT_SUCCESS);
  44. }


代碼L25-L29:初始化定時器的參數,初始間隔與定時間隔。

L32:創建定時器fd,CLOCK_REALTIME:真實時間類型,修改時鐘會影響定時器;CLOCK_MONOTONIC:相對時間類型,修改時鐘不影響定時器。

L35:設置定時器的值。

L44:阻塞等待定時器到期。返回值是未處理的到期次數。比如定時間隔為2秒,但過了10秒才去讀取,則讀取的值是5。

編譯運行:編譯時要加rt庫(g++ -lrt timerfd.cc -o timerfd)

[root@localhost appTest]# ./timerfd 5 2 10
printTime: current time:1357391736.146196 timer started
printTime: current time:1357391741.153430 read: 1; total=1
printTime: current time:1357391743.146550 read: 1; total=2
printTime: current time:1357391745.151483 read: 1; total=3
printTime: current time:1357391747.161155 read: 1; total=4
printTime: current time:1357391749.153934 read: 1; total=5
printTime: current time:1357391751.157309 read: 1; total=6
printTime: current time:1357391753.158384 read: 1; total=7
printTime: current time:1357391755.150470 read: 1; total=8
printTime: current time:1357391757.150253 read: 1; total=9
printTime: current time:1357391759.149954 read: 1; total=10
[root@localhost appTest]#

第一個參數5為第一次定時器到期間隔,第二個參數2為定時器的間隔,第三個參數為定時器到期10次則退出。程序運行(5+2*10)S退出。

詳細信息可以:man timerfd_create

eventfd涉及API:

  1. #include
  2. int eventfd(unsigned int initval, int flags);

創建一個eventfd,這是一個計數器相關的fd,計數器不為零是有可讀事件發生,read以後計數器清零,write遞增計數器;返回的fd可以進行如下操作:read、write、select(poll、epoll)、close。

這個函數會創建一個 事件對象 (eventfd object), 用來實現,進程(線程)間的等待/通知(wait/notify) 機制. 內核會為這個對象維護一個64位的計數器(uint64_t)。並且使用第一個參數(initval)初始化這個計數器。調用這個函數就會返回一個新的文件描述符(event object)。2.6.27版本開始可以按位設置第二個參數(flags)。有如下的一些宏可以使用:

lEFD_NONBLOCK

功能同open(2)的O_NONBLOCK,設置對象為非阻塞狀態,如果沒有設置這個狀態的話,read(2)讀eventfd,並且計數器的值為0 就一直堵塞在read調用當中,要是設置了這個標志, 就會返回一個 EAGAIN 錯誤(errno = EAGAIN)。效果也如同 額外調用select(2)達到的效果。

lEFD_CLOEXEC

這個標識被設置的話,調用exec後會自動關閉文件描述符,防止洩漏。如果是2.6.26或之前版本的內核,flags 必須設置為0。
創建這個對象後,可以對其做如下操作:

1)write:將緩沖區寫入的8字節整形值加到內核計數器上。

2)read:讀取8字節值, 並把計數器重設為0. 如果調用read的時候計數器為0, 要是eventfd是阻塞的, read就一直阻塞在這裡,否則就得到 一個EAGAIN錯誤。如果buffer的長度小於8那麼read會失敗, 錯誤代碼被設置成 EINVAL。

3)poll select epoll

4)close:當不需要eventfd的時候可以調用close關閉, 當這個對象的所有句柄都被關閉的時候,內核會釋放資源。 為什麼不是close就直接釋放呢, 如果調用fork 創建
進程的時候會復制這個句柄到新的進程,並繼承所有的狀態。

l例子

  1. #include
  2. #include
  3. #include
  4. #include
  5. #include
  6. #include
  7. #define handle_error(msg) \
  8. do { perror(msg); exit(1); } while (0)
  9. int main( int argc, char **argv ){
  10. uint64_t u;
  11. ssize_t s;5 int j;
  12. if ( argc < 2 ) {
  13. fprintf(stderr, "input in command argument");
  14. exit(1);
  15. }

  16. int efd;
  17. if ( (efd = eventfd(0, EFD_NONBLOCK)) == -1 )
  18. handle_error("eventfd failed");


  19. switch (fork()) {
  20. case 0:
  21. for( j = 1; j < argc; j ++ ) {
  22. printf("Child writing %s to efd\n", argv[j] );

  23. u = strtoull(argv[j], NULL, 0); /* analogesly atoi */
  24. s = write(efd, &u, sizeof(uint64_t));/*append u to counter */
  25. if ( s != sizeof(uint64_t) )
  26. handle_error("write efd failed");

  27. }
  28. printf("child completed write loop\n");

  29. exit(0);
  30. default:
  31. sleep (2);

  32. printf("parent about to read\n");
  33. s = read(efd, &u, sizeof(uint64_t));
  34. if ( s != sizeof(uint64_t) ) {
  35. if (errno = EAGAIN) {
  36. printf("Parent read value %d\n", s);
  37. return 1;
  38. }
  39. handle_error("parent read failed");
  40. }
  41. printf("parent read %d , %llu (0x%llx) from efd\n",
  42. s, (unsigned long long)u, (unsigned long long) u);
  43. exit(0);

  44. case -1:
  45. handle_error("fork ");
  46. }
  47. return 0;
  48. }

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved