linux文件I/O编程

一、系统调用

所谓的系统的调用时操作系统提供给用户程序调用的一组“特殊”接口,用户可以同个接口获得系统提供的服务。例如用户通过系统调用可以实现创建进程、进程的管理、soket网络通信。

linux用户程序是不能直接访问系统内核提供的服务的。这样做是为了保护系统内核的数据不被修改,保证了系统的安全性。用户空间和内核空间是分离的,通常情况下用户程序不允许访问内核数据和内核的函数。

二、用户编程接口(API)

前面写到用户不是直接和系统进行交互的,而是通过软中断机制向内核提出申请,以获取内核服务的接口。在实际编程中用户调用的是API函数。并不是一个API对应一个系统调用,有的时候一个API 函数对应几个系统的调用。

三、系统命令

系统命令相对于API更高了一层,它实际 上是一个可执行的程序。它们三者之间的关系如下图:


四、linux文件及文件描述符

在linux学习中很重要的一点就是所有的操作都是对文件的操作,内核区分文件就是通过文件描述符。文件描述符是一个非负的整数,它是一个索引值,指向内核中每个进程打开文件的记录表。当打开一个现存文件或新建一个文件时,就会返回一个文件描述符;当读写文件时,也需要将文件描述符作为参数传递给函数。

通常一个进程启动时,会打开三个文件:标准输入,标准输出,标准出错处理。这三个文件分别对应文件描述符0,1,2.(也就是宏替换STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO).

五、不带缓存的I/O操作

下面的第一个程序是向一个文件中写入字符,并按照指定的字节读出来。调用了write、open和read函数。

  1. /*write.c*/  
  2. #include <unistd.h>  
  3. #include <sys/types.h>  
  4. #include <sys/stat.h>  
  5. #include <fcntl.h>  
  6. #include <stdlib.h>  
  7. #include <stdio.h>  
  8. #include <string.h>  
  9. #define MAXSIZE  
  10. int main(void)  
  11. {  
  12.     int i,fd,size,len;  
  13.     char *buf="Hello! I'm writing to this file!";//指针指向的将要写入文件的字符  
  14.     char buf_r[10];  
  15.     len = strlen(buf);//获得字符串的大小  
  16.     buf_r[10] = '\0';  
  17.     //打开文件  
  18.     if((fd = open("/tmp/hello.c", O_CREAT | O_TRUNC | O_RDWR,0666 ))<0){  
  19.         perror("open:");  
  20.         exit(1);  
  21.     }  
  22.     else  
  23.         printf("open file:hello.c %d\n",fd);  
  24.     //写入字符  
  25.     if((size = write( fd, buf, len)) < 0){  
  26.         perror("write:");  
  27.         exit(1);  
  28.     }  
  29.     else  
  30.         printf("Write:%s\n",buf);  
  31.   
  32.     lseek( fd, 0, SEEK_SET ); //定位在起始的位置  
  33.     //读10个字节到buf_r中  
  34.     if((size = read( fd, buf_r, 10))<0){   
  35.         perror("read:");  
  36.         exit(1);  
  37.     }  
  38.     else  
  39.         {  
  40.             printf("read form file:%s\n",buf_r);  
  41.             printf("size = %d\n",size);  
  42.         }  
  43.     if( close(fd) < 0 ){  
  44.         perror("close:");  
  45.         exit(1);  
  46.     }  
  47.     else  
  48.         printf("Close hello.c\n");  
  49.     exit(0);  
  50. }  

程序执行的结果是:


六、fcntl函数

当多个 用户共同使用和操作一个文件时,为了避免出错和进程竞争资源的问题,linux采用给文件上锁的方式。实现上锁的函数式lock和fcntl。其中flock对文件施加建议锁。而fcntl不仅可以施加建议性锁还可以施加强制性锁。同时fcntl还可以对文件上记录锁。

记录锁又分为读取锁和写入锁,其中读取锁又称为共享锁,它能使多个进程在同一时刻对文件建立读取锁。写入锁又称为排斥锁,在任意时刻只有一个进程可以对文件的某一部分施加写入锁。当然,在文件的同一部分不能同时建立写入锁和读取锁。


下面是测试文件的写入锁,首先创建一个hello文件,然后加上写入锁,写入之后释放写入锁。

  1. #include <unistd.h>  
  2. #include <sys/file.h>  
  3. #include <sys/types.h>  
  4. #include <sys/stat.h>  
  5. #include <stdio.h>  
  6. #include <stdlib.h>  
  7.   
  8. void lock_set(int fd, int type)  
  9. {  
  10.     struct flock lock;  
  11.     //加锁整个文件  
  12.     lock.l_whence = SEEK_SET;  
  13.     lock.l_len =0;  
  14.     while(1){  
  15.         lock.l_type = type;  
  16.           
  17.         if((fcntl(fd, F_SETLK, &lock)) == 0){  
  18.         if( lock.l_type == F_RDLCK )  
  19.             printf("read lock set by %d\n",getpid());  
  20.         else if( lock.l_type == F_WRLCK )  
  21.             printf("write lock set by %d\n",getpid());  
  22.         else if( lock.l_type == F_UNLCK )  
  23.             printf("release lock by %d\n",getpid());  
  24.         return;  
  25.         }  
  26.         fcntl(fd, F_GETLK,&lock);  
  27.         if(lock.l_type != F_UNLCK){  
  28.             if( lock.l_type == F_RDLCK )   
  29.                 printf("read lock already set by %d\n",lock.l_pid);  
  30.             else if( lock.l_type == F_WRLCK )  
  31.                 printf("write lock already set by %d\n",lock.l_pid);  
  32.             getchar();  
  33.         }  
  34.     }  
  35. }  
  36.   
  37. int main(void)  
  38. {  
  39.     int fd;  
  40.     fd=open("hello",O_RDWR | O_CREAT, 0666);  
  41.     if(fd < 0){  
  42.         perror("open");  
  43.         exit(1);  
  44.     }  
  45.     lock_set(fd, F_WRLCK);  
  46.     getchar();  
  47.     lock_set(fd, F_UNLCK);  
  48.     getchar();  
  49.     close(fd);  
  50.     exit(0);  
  51. }  

为了更好的展示写入锁的效果,可以打开两个终端执行这个程序,看程序的运行的结果。

终端1运行的结果:




终端2运行的结果:


七、总的来说I/O处理的模型有5种。

1、阻塞I/O模型:在这种模型下,若所调用的I/O函数没有完成相关的功能,就会将进程挂起,直到相关数据到才会出错返回。如常见的对管道设备、终端设备和网络设备进行读写时就会经常出现这种问题。

2、非阻塞模型:这种模型下,当情求的I/O不能完成时,则不让进程睡眠,而是返回一个错误。

3、I/O多路转接模型:在这种模型下,如果请求的I/O阻塞,且它不是真正的阻塞,而是让其中的一个函数等待,在这期间,I/O还能继续其他的操作。select函数就是属于这种模型。

4、信号驱动I/O模型:在这种模型下,通过安装一个信号处理程序,系统可以自动捕获特定信号的到来,从而启动I/O。这是由内核通知用户何时何时启动I/O操作。

5、异步I/O模型:在这种模型下,当一个描述符已经准备好,可以启动I/O时,进程会通知内核。现在,并不是所有的内核都支持这种模型。

可以看到select的多路转接模型是处理I/O复用的一个高效的方式。它可以具体设置每一个具体所关心的文件描述符的条件、等待的时间。


本例中是将hello1的内容读出,并将此内容每隔10秒写入hello2中。在这里建立了两个描述符集,其中一个描述符inset1用于读取文件,另一个inset2用于写文件。在初始化文件描述符之后,就循环的测试这两个文件是否可读写,由于这里没有阻塞,所以文件描述符处于准备就绪状态。这时对两个描述符进行fds[0]和fds[1]进行读写操作。

  1. /*select.c*/  
  2. #include <fcntl.h>  
  3. #include <stdio.h>  
  4. #include <unistd.h>  
  5. #include <stdlib.h>  
  6. #include <time.h>  
  7.   
  8. int main(void)  
  9. {  
  10.     int fds[2];  
  11.     char buf[7];  
  12.     int i,rc,maxfd;  
  13.     fd_set inset1,inset2;  
  14.     struct timeval tv;  
  15.     //以一定的权限打开文件hello1  
  16.     if((fds[0] = open ("hello1", O_RDWR|O_CREAT,0666))<0)  
  17.         perror("open hello1");  
  18.         //以一定的权限打开文件hello2  
  19.     if((fds[1] = open ("hello2", O_RDWR|O_CREAT,0666))<0)  
  20.         perror("open hello2");  
  21.         //将字符写入hello1文件  
  22.     if((rc = write(fds[0],"Hello!\n",7)))  
  23.         printf("rc=%d\n",rc);  
  24.         //将文件指针恢复在起始的位置  
  25.     lseek(fds[0],0,SEEK_SET);  
  26.     //取出两个文件描述符较大者  
  27.     maxfd = fds[0]>fds[1] ? fds[0] : fds[1];  
  28.     //初始化读集合inset1,并在读集合中加入描述集  
  29.     FD_ZERO(&inset1);   
  30.     FD_SET(fds[0],&inset1);  
  31.     //初始化写集合inset2,并在写集合中加入描述集  
  32.     FD_ZERO(&inset2);  
  33.     FD_SET(fds[1],&inset2);  
  34.       
  35.     tv.tv_sec=2;  
  36.     tv.tv_usec=0;  
  37.     //循环的测试这两个文件是否可读写  
  38.     while(FD_ISSET(fds[0],&inset1)||FD_ISSET(fds[1],&inset2)){   
  39.         if(select(maxfd+1,&inset1,&inset2,NULL,&tv)<0)   
  40.             perror("select");  
  41.         else{  
  42.             if(FD_ISSET(fds[0],&inset1)){  
  43.                 rc = read(fds[0],buf,7);  
  44.                 if(rc>0){  
  45.                     buf[rc]='\0';  
  46.                     printf("read: %s\n",buf);  
  47.                 }else  
  48.                     perror("read");  
  49.             }  
  50.             if(FD_ISSET(fds[1],&inset2)){  
  51.                 rc = write(fds[1],buf,7);  
  52.                 if(rc>0){  
  53.                     buf[rc]='\0';  
  54.                     printf("rc=%d,write: %s\n",rc,buf);  
  55.                 }else  
  56.                     perror("write");  
  57.             sleep(10);  
  58.             }  
  59.         }  
  60.     }  
  61.     exit(0);  
  62. }  

程序运行的结果:


程序运行的流程:



版权所有,属于华清远见。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值