1. 文件的基本操作及空洞文件
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main( void )
{
int fd;
int n;
char buf[ 10 ] = "abcdefghij";
char buf1[ 10 ] = "ABCDEFGHIJ";
if ( ( fd = open( "test", O_RDWR | O_CREAT ) ) < 0 )
printf( "open test file error\n" );
if ( write( fd, buf, 10 ) != 10 )
printf( "write error\n" );
if ( ( n = lseek( fd, 40, SEEK_SET ) ) == -1 )
printf( "lseek error\n" );
if ( write( fd, buf1, 10 ) != 10 )
printf( "write error\n" );
if ( lseek( fd, 0, SEEK_SET ) == -1 )
printf( "lseek error\n" );
return 0;
}
程序输出:
2. 原子操作
考虑多个进程对同一个文件进行读取的时候,会造成以下后果:进程A在索引lseek到100,然后进程B也lseek到100,接着写入数据。然后进程A继续写入文件,则会覆盖掉进程B写入的数据。我们通过父进程,子进程进行模拟操作。
但是父进程和子进程是共享资源的,所以无法很好的解释。
#include <stdio.h>
#include <fcntl.h>
int main( void )
{
int fd;
int n;
pid_t pid;
char buf1[] = "abcdefghi\n";
char buf2[] = "ABCDEFGHI\n";
if ( ( fd = open( "test", O_RDWR ) ) == -1 ){
printf("open file error\n");
}
if ( write( fd, buf1, 10 ) != 10 ){
printf( "parent write error\n" );
}
if ( ( pid = fork() ) < 0 ){
printf("fork error\n");
} else if ( pid == 0 ){
if ( ( fd = open( "test", O_RDWR ) ) == -1 ){
printf("open file error\n");
}
lseek( fd, 5, SEEK_SET );
if ( write( fd, buf2, 10 ) != 10 ){
printf("child write error\n");
}
}
if ( write( fd, buf1, 10 ) != 10 ){
printf( "parent write error\n" );
}
return 0;
}
程序输出:
父进程第一次写入abcdefghi,然后子进程从第五字节开始覆盖写入,由于父进程和子进程共享资源,所以父进程继续在文件的末尾写入。
如果是两个不同的进程,则程序输入应该是:abcdeABCDEacbdefghi。两个进程会对文件进行互相的覆盖。
3. dup和dup2的操作
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main( void )
{
int fd1;
int fd2;
char buf[ 1024 ];
int n;
if ( ( fd1 = dup( 0 ) ) == -1 ){
printf("dup error\n");
}
printf("%d\n", fd1 );
if ( ( fd2 = open( "test.out", O_RDWR ) ) == -1 ){
printf("open file error");
}
if ( dup2( 1, fd2 ) < 0 ){
printf("dup2 error\n");
}
while ( ( n = read( fd1, buf, 1024 ) ) > 0 ){
if ( write( fd2, buf, n ) != n ){
printf( "write error\n" );
}
}
if ( n < 0 ){
printf("read error\n");
}
return 0;
}
1) 将标准输入流0定位到fd1中,将文件fd2定位到标准输出流1中(这里并不是重定向,因为数据不仅写入fd2中,还显示在界面上)
2) 将数据从fd1中读取,写入fd2中,则相当于从标准输入流中读取数据,写入到标准输出流中。
程序输出:
4. fcntl函数
fcntl用于改变已打开的文件的性质。
#include <stdio.h>
#include <fcntl.h>
int main( int argc, char *argv[] )
{
int fd;
int val;
if ( argc != 2 )
printf("usage:a.out <descriptor#>");
if ( ( val = fcntl( atoi( argv[ 1 ] ) , F_GETFL, 0 ) ) < 0 )
printf("fcntl error for fd %d", atoi( argv[ 1 ] ) );
switch( val & O_ACCMODE ){
case O_RDONLY:
printf("read only");
break;
case O_WRONLY:
printf("write only");
break;
case O_RDWR:
printf("read write");
break;
default:
printf("unknown access mode");
break;
}
if ( val & O_APPEND )
printf(",append");
if ( val & O_NONBLOCK )
printf(",nonblocking");
#if defined( O_SYNC )
if ( val & O_SYNC )
printf( ", synchronous writes");
#endif
#if !defined(_POSIX_C_SOURCE ) && defined( O_FDYNC )
if ( val & O_FSYNC )
printf(",synchronous writes" );
#endif
putchar('\n');
return 0;
}
程序输出:
修改文件描述符标志或文件状态标志时候要小心,先要取得现有的标志值,然后根据需要修改它,最后设置新标志值。不能只是执行F_SETFD或F_SETFL命令,这样会关闭以前设置的标志位的。
void set_fl( int fd, int flags )
{
int val;
if ( ( val = fcntl( fd, F_GETFL, 0 ) ) < 0 )
printf("fcntl F_GETFL error");
val |= flags;
if ( fcntl(fd, F_SETFL, val ) < 0 )
printf("fcntl F_SETFL error");
}