Linux 文件I/O(一)之入门及练习源码
Linux环境下,所有东西都以文件的形式存在。应用软件如果想操作一个文件,必须通过系统调用获取更高的权限。即可以直接通过系统调用操作文件如open(2),也可以通过库函数间接的使用系统调用如fopen(3)。
库函数包含了部分系统调用。一方面把系统调用抽象了,一方面方便了用户级的调用。系统调用属于内核态,库函数属于用户态。
对于文件打开来说,fopen(3)分配一个FILE结构体,其中包含该文件的文件描述符,I/O缓冲区等信息,返回这个FILE结构体的地址。对于C标准I/O库,打开的文件由FILE*指针标记,而对于内核来说打开的文件由描述符标记。
每个进程在linux内核中都有一个task_struct结构体来维护进程相关信息,称为进程描述符,每个task_struct中都有一个指针指向file_struct结构体,称为文件描述符表,其中每个表项包含一个指向已打开的文件的指针。
1.file_cpy.c 实现文件的拷贝
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <strings.h>
int file_cpy(int fd_src,int fd_des)
{
int sret,dret;
char str[128];
char *p;
bzero(str,128);
while((sret=read(fd_src,str,128))>0){
p=str;
while(sret){
dret=write(fd_des,p,sret);
sret=sret-dret;
p=p+dret;
}
bzero(str,128);
}
return 0;
}
int main(int argc,char *argv[])
{
int fd_src;
int fd_des;
if(argc!=3)
printf("format error\n");
fd_src=open(argv[1],O_RDONLY);
if(fd_src<0)
perror("open fd_src error");
fd_des=open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0666);
if(fd_des<0)
perror("open fd_des error\n");
file_cpy(fd_src,fd_des);
close(fd_des);
close(fd_src);
return 0;
}
此程序需注意的是当你从一个字符数组向一个文件write时,并不是你想写的都会成功写进去,这个问题可能开始不会出现,但当你写的次数足够多时就会出现问题,所以要注意把没写进去的内容再写进文件中。
2.read_block.c 终端阻塞读
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
int n;
char buf[10];
n=read(STDIN_FILENO,buf,10);
if(n<0)
perror("read");
write(STDOUT_FILENO,buf,n);
return 0;
}
3.read_noblock.c 非堵塞读
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#define MSG_TRY "try again\n"
#define MSG_TIMEOUT "timeout\n"
int main(void)
{
char buf[10];
int fd,n,i;
fd=open("/dev/tty",O_RDONLY|O_NONBLOCK);
if(fd<0){
perror("open /dev/tty");
exit(1);
}
for(i=0; i<5; i++){
n=read(fd,buf,10);
if(n>=0)
break;
if(errno!=EAGAIN){
perror("read /dev/tty");
exit(1);
}
sleep(1);
write(STDOUT_FILENO,MSG_TRY,strlen(MSG_TRY));
}
if(i==5)
write(STDOUT_FILENO,MSG_TIMEOUT,strlen(MSG_TIMEOUT));
else
write(STDOUT_FILENO,buf,n);
close(fd);
return 0;
}
4.mmap内存映射
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>
int main(void)
{
int *p;
int fd;
fd=open("hello",O_RDWR);
if(fd<0){
perror("open hello:");
exit(1);
}
p=mmap(NULL,6,PROT_WRITE,MAP_SHARED,fd,0);
if(p==MAP_FAILED){
perror("mmap");
exit(1);
}
close(fd);
p[0]=0x30313233;
munmap(p,6);
return 0;
}
文件映射到内存时文件位置与内存位置是对应的,但内存是以页为单位管理内存的,所以内存一分配最小就是一页大小。我遇到一个问题,举个例子,如果你的文件里面有6个字符,你将这6个字符映射到内存,你向此内存写入8个字符,但文件中还是只有6个字节。
5.ioctl
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
struct winsize size;
if(isatty(STDOUT_FILENO)==0)
exit(1);
if(ioctl(STDOUT_FILENO,TIOCGWINSZ,&size)<0){
perror("ioctl TIOCGWINSZ");
exit(1);
}
printf("%d rows ,%d col\n",size.ws_row,size.ws_col);
return 0;
}
6.fcntl
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
int val;
if(argc!=2){
fputs("usage:a.out <descriptor#>\n",stderr);
exit(1);
}
if((val=fcntl(atoi(argv[1]),F_GETFL))<0){
printf("fcntl error for fd %d\n",atoi(argv[1]));
exit(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:
fputs("invalid access mode\n",stderr);
exit(1);
}
if(val & O_APPEND)
printf(", append");
if(val & O_NONBLOCK)
printf(", nonblocking");
putchar('\n');
return 0;
}
以上就是自己在学UNIX高编文件I/O入门练习的几个小代码,后面继续讲解!