慢系統調用(slow system call):此術語適用於那些可能永遠阻塞的系統調用。永遠阻塞的系統調用是指調用有可能永遠無法返回,多數網絡支持函數都屬於這一類。如:若沒有客戶連接到服務器上,那么服務器的accept調用就沒有返回的保證。
EINTR錯誤的產生:當阻塞於某個慢系統調用的一個進程捕獲某個信號且相應信號處理函數返回時,該系統調用可能返回一個EINTR錯誤。例如:在socket服務器端,設置了信號捕獲機制,有子進程,當在父進程阻塞於慢系統調用時由父進程捕獲到了一個有效信號時,內核會致使accept返回一個EINTR錯誤(被中斷的系統調用)。
當碰到EINTR錯誤的時候,可以采取有一些可以重啟的系統調用要進行重啟,而對於有一些系統調用是不能夠重啟的。例如:accept、read、write、select、和open之類的函數來說,是可以進行重啟的。不過對於套接字編程中的connect函數我們是不能重啟的,若connect函數返回一個EINTR錯誤的時候,我們不能再次調用它,否則將立即返回一個錯誤。針對connect不能重啟的處理方法是,必須調用select來等待連接完成。
在 linux 或者 unix 環境中, errno 是一個十分重要的部分。在調用的函 數出現問題的時候,我們可以通過 errno 的值來確定出錯的原因,這就會 涉及到一個問題,那就是如何保證 errno 在多線程或者進程中安全?我們希望在多線程或者進程中,每個線程或者進程都擁有自己獨立和唯一的一個 errno ,這樣就能夠保證不會有競爭條 件的出現。一般而言,編譯器會自動保證 errno 的安全性,但是為了妥善期間,我們希望在寫 makefile 的時 候把 _LIBC_REENTRANT 宏定義,比 如我們在檢查 <bits/errno.h> 文件中發現如下的定義:
C代碼
# ifndef __ASSEMBLER__
/* Function to get address of global `errno' variable. */
extern int *__errno_location (void) __THROW __attribute__ ((__const__));
# if !defined _LIBC || defined _LIBC_REENTRANT
/* When using threads, errno is a per-thread value. */
# define errno (*__errno_location ())
# endif
# endif /* !__ASSEMBLER__ */
#endif /* _ERRNO_H */
也就是說,在沒有定義 __LIBC 或者定義 _LIBC_REENTRANT 的時候, errno 是多線程 / 進程安全的。
一般而言, __ASSEMBLER__, _LIBC 和 _LIBC_REENTRANT 都不會被編譯器定義,但是如果我們定義 _LIBC_REENTRANT 一次又何妨那?
為了檢測一下你編譯器是否定義上述變量,不妨使用下面一個簡單程序。
C代碼
#include <stdio.h>
#include <errno.h>
int main( void )
{
#ifndef __ASSEMBLER__
printf( "Undefine __ASSEMBLER__\n" );
#else
printf( "define __ASSEMBLER__\n" );
#endif
#ifndef __LIBC
printf( "Undefine __LIBC\n" );
#else
printf( "define __LIBC\n" );
#endif
#ifndef _LIBC_REENTRANT
printf( "Undefine _LIBC_REENTRANT\n" );
#else
printf( "define _LIBC_REENTRANT\n" );
#endif
return 0;
}
希望讀者在進行移植的時候,讀一下相關的 unix 版本的 <bits/errno.h> 文 件,來確定應該定義什么宏。不同的 unix 版本可能存在着一些小的差別!
有時候,在調用系統調用時,可能會接收到某個信號而導致調用退出。譬如使用system調用某個命令之后該進程會接收到SIGCHILD信號,然后如果這個進程的線程中有慢系統調用,那么接收到該信號的時候可能就會退出,返回EINTR錯誤碼。
EINTR
linux中函數的返回狀態,在不同的函數中意義不同:
1)write
表示:由於信號中斷,沒寫成功任何數據。
The call was interrupted by a signal before any data was written.
2)read
表示:由於信號中斷,沒讀到任何數據。
The call was interrupted by a signal before any data was read.
3)sem_wait
函數調用被信號處理函數中斷
The call was interrupted by a signal handler.
4)recv
由於信號中斷返回,沒有任何數據可用。
function was interrupted by a signal that was caught, before any data was available.
調用系統調用的時候,有時系統調用會被中斷.此時,系統調用會返回-1,並且錯誤碼被置為EINTR.但是,有時並不將這樣的情況作為錯誤.有兩種處理方法:
1.如果錯誤碼為EINTR則重新調用系統調用,例如Postgresql中有一段代碼:
view plain
copy to clipboard
print
?
retry1:
if (send(port->sock, &SSLok, 1, 0) != 1)
{
if (errno == EINTR)
goto retry1; /* if interrupted, just retry */
[cpp] view plain
copy
retry1:
if (send(port->sock, &SSLok, 1, 0) != 1)
{
if (errno == EINTR)
goto retry1; /* if interrupted, just retry */
2.重新定義系統調用,忽略錯誤碼為EINTR的情況.例如,Cherokee中的一段代碼:
view plain
copy to clipboard
print
?
int
cherokee_stat (const char *restrict path, struct stat *buf)
{
int re;
do {
re = stat (path, buf);
} while ((re == -1) && (errno == EINTR));
return re;
}
最近 socket 讀數據老是遇到 Interrupted system call (EINTR),代碼改為如下解決
while (1)
{
select(socket+1, &readfds, NULL, NULL, &tv);
if (FD_ISSET(socket, &readfds))
{
printf("connection got!\n");
break;
}
else{
if (errno == EINTR)
continue;
else
printf("Timed out.\n");
}
}
下面的列表顯示常見的 Linux 系統錯誤代碼。
1 EPERM
Operation not permitted
操作不許可
2 ENOENT
No such file or directory
無此文件或目錄
3 ESRCH
No such process
無此過程
4 EINTR
Interrupted system call
系統調用被禁止
5 EIO
I/O error
I/O 錯誤
6 ENXIO
No such device or address
無此器件或地址
7 E2BIG
Arg list too long
Arg 列表太長
8 ENOEXEC
Exec format error
Exec 格式錯誤
9 EBADF
Bad file number
文件數目錯誤
10 ECHILD
No child processes
無子過程
11 EAGAIN
Try again
再試一遍
12 ENOMEM
Out of memory
內存溢出
13 EACCES
Permission denied
許可拒絕
14 EFAULT
Bad address
錯誤的地址
15 ENOTBLK
Block device required
需要塊設備
16 EBUSY
Device or resource busy
設備或資源忙
17 EEXIST
File exists
文件存在
18 EXDEV
Cross-device link
跨器鏈接
19 ENODEV
No such device
無此設備
20 ENOTDIR
Not a directory
不是一個目錄
21 EISDIR
Is a directory
是一個目錄
22 EINVAL
Invalid argument
無效的函數自變量
23 ENFILE
File table overflow
文件表溢出
24 EMFILE
Too many open files
打開的文件太多
25 ENOTTY
Inappropriate ioctl for device
26 ETXTBSY
Text file busy
文本文件忙
27 EFBIG
File too large
文件太大
28 ENOSPC
No space left on device
磁盤空間不足
29 ESPIPE
Illegal seek
不合法的尋找
30 EROFS
Read-only file system
只讀文件系統
31 EMLINK
Too many links
太多的鏈接