日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

信号中断 与 慢系统调用

發布時間:2023/12/10 windows 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 信号中断 与 慢系统调用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 術語

1.1. 慢系統調用(Slow system call)

該術語適用于那些可能永遠阻塞的系統調用。永遠阻塞的系統調用是指調用永遠無法返回,多數網絡支持函數都屬于這一類。如:若沒有客戶連接到服務器上,那么服務器的accept調用就會一直阻塞。

慢系統調用可以被永久阻塞,包括以下幾個類別:

(1)讀寫‘慢’設備(包括pipe,終端設備,網絡連接等)。讀時,數據不存在,需要等待;寫時,緩沖區滿或其他原因,需要等待。讀寫磁盤文件一般不會阻塞。

(2)當打開某些特殊文件時,需要等待某些條件,才能打開。例如:打開中斷設備時,需要等到連接設備的modem響應才能完成。

(3)pause和wait函數。pause函數使調用進程睡眠,直到捕獲到一個信號。wait等待子進程終止。

(4)某些ioctl操作。

(5)某些IPC操作。

2. EINTR介紹

2.1. EINTR錯誤產生的原因

早期的Unix系統,如果進程在一個慢系統調用(slow system call)中阻塞時,當捕獲到某個信號且相應信號處理函數返回時,這個系統調用被中斷,調用返回錯誤,設置errno為EINTR(相應的錯誤描述為“Interrupted system call”)。

怎么看哪些系統條用會產生EINTR錯誤呢?用man啊!

如下表所示的系統調用就會產生EINTR錯誤,當然不同的函數意義也不同。

?

系統調用函數

errno為EINTR表征的意義

write

由于信號中斷,沒寫成功任何數據。

The call was interrupted by a signal before any data was written.

open

由于信號中斷,沒讀到任何數據。

The call was interrupted by a signal before any data was read.

recv

由于信號中斷返回,沒有任何數據可用。

The receive was interrupted by delivery of a signal before any data were available.

sem_wait

函數調用被信號處理函數中斷。

The call was interrupted by a signal handler.

?

2.2. 如何處理被中斷的系統調用

既然系統調用會被中斷,那么別忘了要處理被中斷的系統調用。有三種處理方式:

◆ 人為重啟被中斷的系統調用

◆ 安裝信號時設置 SA_RESTART屬性(該方法對有的系統調用無效)

◆? 忽略信號(讓系統不產生信號中斷)

2.2.1. 人為重啟被中斷的系統調用

人為當碰到EINTR錯誤的時候,有一些可以重啟的系統調用要進行重啟,而對于有一些系統調用是不能夠重啟的。例如:accept、read、write、select、和open之類的函數來說,是可以進行重啟的。不過對于套接字編程中的connect函數我們是不能重啟的,若connect函數返回一個EINTR錯誤的時候,我們不能再次調用它,否則將立即返回一個錯誤。針對connect不能重啟的處理方法是,必須調用select來等待連接完成。

這里的“重啟”怎么理解?

一些IO系統調用執行時,如 read 等待輸入期間,如果收到一個信號,系統將中斷read, 轉而執行信號處理函數. 當信號處理返回后, 系統遇到了一個問題: 是重新開始這個系統調用, 還是讓系統調用失敗?早期UNIX系統的做法是, 中斷系統調用,并讓系統調用失敗, 比如read返回 -1, 同時設置 errno 為EINTR中斷了的系統調用是沒有完成的調用,它的失敗是臨時性的,如果再次調用則可能成功,這并不是真正的失敗,所以要對這種情況進行處理, 典型的方式為:

[cpp]?view plaincopy
  • again:??
  • ??????????if?((n?=?read(fd,?buf,?BUFFSIZE))?<?0)?{??
  • ?????????????if?(errno?==?EINTR)??
  • ??????????????????goto?again;?????/*?just?an?interrupted?system?call?*/??
  • ????????????/*?handle?other?errors?*/??
  • ??????????}??
  • 可以去github上看看別人怎么處理EINTR錯誤的。在github上搜索“==EINTR”關鍵字就有一大堆了。摘取幾個看看:

    [cpp]?view plaincopy
  • ……??
  • ???
  • while?((r?=?read?(fd,?buf,?len))?<?0?&&?errno?==?EINTR)?/*do?
  • nothing*/?;??
  • ???
  • ……??

  • [cpp]?view plaincopy
  • ssize_t?Read(int?fd,?void?*ptr,?size_t?nbytes)??
  • {??
  • ???
  • ????????ssize_t?n;??
  • ???
  • again:??
  • ????????if((n?=?read(fd,?ptr,?nbytes))?==?-1){??
  • ????????????????if(errno?==?EINTR)??
  • ????????????????????????goto?again;??
  • ????????????????else??
  • ????????????????????????return?-1;??
  • ????????}??
  • ????????return?n;??
  • }??
  • 2.2.2. 安裝信號時設置 SA_RESTART屬性

    ?我們還可以從信號的角度來解決這個問題,? 安裝信號的時候, 設置 SA_RESTART屬性,那么當信號處理函數返回后, 不會讓系統調用返回失敗,而是讓被該信號中斷的系統調用將自動恢復。

    [cpp]?view plaincopy
  • struct?sigaction?action;??
  • ???
  • action.sa_handler?=?handler_func;??
  • sigemptyset(&action.sa_mask);??
  • action.sa_flags?=?0;??
  • /*?設置SA_RESTART屬性?*/??
  • action.sa_flags?|=?SA_RESTART;??
  • ???
  • sigaction(SIGALRM,?&action,?NULL);??
  • 但注意,并不是所有的系統調用都可以自動恢復。如msgsnd喝msgrcv就是典型的例子,msgsnd/msgrcv以block方式發送/接收消息時,會因為進程收到了信號而中斷。此時msgsnd/msgrcv將返回-1,errno被設置為EINTR。且即使在插入信號時設置了SA_RESTART,也無效。在man msgrcv中就有提到這點:

    msgsnd and msgrcv are never automatically restarted after being interrupted by a signal handler, regardless of the setting? of the SA_RESTART flag when establishing a signal? handler.

    ?

    2.2.3. 忽略信號

    當然最簡單的方法是忽略信號,在安裝信號時,明確告訴系統不會產生該信號的中斷。

    [cpp]?view plaincopy
  • struct?sigaction?action;??
  • ???
  • action.sa_handler?=?SIG_IGN;??
  • sigemptyset(&action.sa_mask);??
  • ???
  • sigaction(SIGALRM,?&action,?NULL);??
  • 3. 測試代碼

    為了方便大家測試,這里附上兩段測試代碼。

    3.1. 測試代碼一

    鬧鐘信號SIGALRM中斷read系統調用。安裝SIGALRM信號時如果不設置SA_RESTART屬性,信號會中斷read系統過調用。如果設置了SA_RESTART屬性,read就能夠自己恢復系統調用,不會產生EINTR錯誤。

    [cpp]?view plaincopy
  • #include?<signal.h>??
  • #include?<stdio.h>??
  • #include?<stdlib.h>??
  • #include?<error.h>??
  • #include?<string.h>??
  • #include?<unistd.h>??
  • ???
  • void?sig_handler(int?signum)??
  • {??
  • ????printf("in?handler\n");??
  • ????sleep(1);??
  • ????printf("handler?return\n");??
  • }??
  • ???
  • int?main(int?argc,?char?**argv)??
  • {??
  • ????char?buf[100];??
  • ????int?ret;??
  • ????struct?sigaction?action,?old_action;??
  • ???
  • ????action.sa_handler?=?sig_handler;??
  • ????sigemptyset(&action.sa_mask);??
  • ????action.sa_flags?=?0;??
  • ????/*?版本1:不設置SA_RESTART屬性?
  • ?????*?版本2:設置SA_RESTART屬性?*/??
  • ????//action.sa_flags?|=?SA_RESTART;??
  • ???
  • ????sigaction(SIGALRM,?NULL,?&old_action);??
  • ????if?(old_action.sa_handler?!=?SIG_IGN)?{??
  • ????????sigaction(SIGALRM,?&action,?NULL);??
  • ????}??
  • ????alarm(3);??
  • ?????
  • ????bzero(buf,?100);??
  • ???
  • ????ret?=?read(0,?buf,?100);??
  • ????if?(ret?==?-1)?{??
  • ????????perror("read");??
  • ????}??
  • ???
  • ????printf("read?%d?bytes:\n",?ret);??
  • ????printf("%s\n",?buf);??
  • ???
  • ????return?0;??
  • }??
  • 3.2. 測試代碼二

    鬧鐘信號SIGALRM中斷msgrcv系統調用。即使在插入信號時設置了SA_RESTART,也無效。

    [cpp]?view plaincopy
  • #include?<stdio.h>??
  • #include?<stdlib.h>??
  • #include?<unistd.h>??
  • #include?<errno.h>??
  • #include?<signal.h>??
  • #include?<sys/types.h>??
  • #include?<sys/ipc.h>??
  • #include?<sys/msg.h>??
  • ???
  • void?ding(int?sig)??
  • {??
  • ????printf("Ding!\n");??
  • }??
  • ???
  • struct?msgst??
  • {??
  • ????long?int?msg_type;??
  • ????char?buf[1];??
  • };??
  • ???
  • int?main()??
  • {??
  • ????int?nMsgID?=?-1;??
  • ???
  • ????//?捕捉鬧鐘信息號??
  • ????struct?sigaction?action;??
  • ????action.sa_handler?=?ding;??
  • ????sigemptyset(&action.sa_mask);??
  • ????action.sa_flags?=?0;??
  • ????//?版本1:不設置SA_RESTART屬性??
  • ????//?版本2:設置SA_RESTART屬性??
  • ????action.sa_flags?|=?SA_RESTART;??
  • ????sigaction(SIGALRM,?&action,?NULL);??
  • ?????
  • ????alarm(3);??
  • ????printf("waiting?for?alarm?to?go?off\n");??
  • ???
  • ????//?新建消息隊列??
  • ????nMsgID?=?msgget(IPC_PRIVATE,?0666?|?IPC_CREAT);??
  • ????if(?nMsgID?<?0?)??
  • ????{??
  • ????????perror("msgget?fail"?);??
  • ????????return;??
  • ????}??
  • ????printf("msgget?success.\n");??
  • ???
  • ????//?阻塞?等待消息隊列??
  • ????//??
  • ????//?msgrcv會因為進程收到了信號而中斷。返回-1,errno被設置為EINTR。??
  • ????//?即使在插入信號時設置了SA_RESTART,也無效。man?msgrcv就有說明。??
  • ????//??
  • ????struct?msgst?msg_st;??
  • ????if(?-1?==?msgrcv(?nMsgID,?(void*)&msg_st,?1,?0,?0?)?)??
  • ????{??
  • ????????perror("msgrcv?fail");??
  • ????}??
  • ???
  • ????printf("done\n");??
  • ???
  • ????exit(0);??
  • }??
  • 4. 總結

    慢系統調用(slow system call)會被信號中斷,系統調用函數返回失敗,并且errno被置為EINTR(錯誤描述為“Interrupted system call”)。

    處理方法有以下三種:①人為重啟被中斷的系統調用;②安裝信號時設置 SA_RESTART屬性;③忽略信號(讓系統不產生信號中斷)。

    有時我們需要捕獲信號,但又考慮到第②種方法的局限性(設置 SA_RESTART屬性對有的系統無效,如msgrcv),所以在編寫代碼時,一定要“人為重啟被中斷的系統調用”。

    ?

    總結

    以上是生活随笔為你收集整理的信号中断 与 慢系统调用的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。