星期二, 3月 21, 2006

file and stream : stdin and STDIN_FILENO

man stdin 時出現的,剛好,我一直對file descriptor和stream有疑問:
Since FILEs are a buffering wrapper around Unix file descriptors, the same underlying files may also be accessed
using the raw Unix file interface, that is, the functions like read(2) and lseek(2). The integer file descriptors
associated with the streams stdin, stdout, and stderr are 0, 1, and 2, respectively. The preprocessor symbols
STDIN_FILENO, STDOUT_FILENO, and STDERR_FILENO are defined with these values in <unistd.h>.
fd是file unix type的file descriptor。FILE 則是在unix type的file io加上buffer。
一般application在執行時,系統就已經開好stdin,stdout, stderr這三個 standard io stream 。
所以不用宣告,就可以用(其實是include了stdio.h)。

至於unix type的file handle,對應的就是STDIN_FILENO, STDOUT_FILENO。STDERR_FILENO。

要注意unix type和stream type的read/write 各有自己的function。

要把unix type file descriptor轉成STREAM type FILE *,用fdopen()。



在man stdin中有說明很多file descriptor和FILE (stream)的差異。
CONSIDERATIONS:

stream stderr是unbuffered,stdout是line buffered,也就是說buffer的data在遇到fflush, exit 或是 "\n"時,才會真的送到file中。
所以測試dup2( )的code:
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>

int main(int argc,char **argv)
{
int fd;

printf("Start!\n");
fd = open("outfile", O_CREAT | O_RDWR | O_TRUNC, 0444);
printf("open fd %0x ",fd);
fflush(stdout);

dup2(fd,STDOUT_FILENO);
printf("Can you see this ?\n");

sleep(1);
close(fd);

exit(0);
}
因 為printf("open fd..這一行沒有new line: \n,所以實際上"open fd "會到了printf("Can you see...這一行,遇到 \n 才會寫入stdout。這時的 stdout是 open 的file : output了。
所以在dup2( ),變更stdout前,用fflush(stdout) 強制將所有stdout buffer中的資料寫入device中。

如果要改變stdin/out的特性,例如:變更buffer的行為,echo on/off...etc。就要用tcsetattr( )。
這個function屬於termio.

用tcgetattr( )取得目前terminal設定值,修改後,用tcsetattr( )設定。

這是從sample code看到的,sample code可以catch到一個按鍵動作,不需要return鍵,但是我的卻需要。---- 同樣是用read(STDIN_FILENO)讀取stdin。

所以猜他改了stdin的行為,往上查,發現tcsetattr( )。

要做到沒有buffer的動作,大概要如下的方法(同時將特殊按鍵轉為3 key,disable echo:
main()
{
char buf[3];
int cnt;
struct termios tio_org;
struct termios tio_new;

tcgetattr(STDIN_FILENO,&tio_org);
tio_new =tio_org;
tio_new.c_lflag &= ~( ECHO | ICANON | ISIG );
tio_new.c_iflag &= ~( BRKINT );

tio_new.c_cc[VMIN ]=3;
tio_new.c_cc[VTIME]=1;
tcsetattr(STDIN_FILENO,TCSAFLUSH,&tio_new);

while(1){
cnt = read(STDIN_FILENO,buf,3);
if(cnt >0) {
printf("cnt = %d\n",cnt);
buf[cnt]=0;
printf("%s",buf);
break;
}
}

tcsetattr(STDIN_FILENO,TCSAFLUSH,&tio_org);
}
.這樣,按下任一鍵,馬上就能偵測到。
其中,ICANON和c_cc[ ]的設定是重點。

沒有留言:

網誌存檔