前言
Linux的核心就是一切皆文件,所以Linux下的文件编程至关重要
文件IO
普通的IO操作一般会被内核缓存,这种IO被称作缓存IO
Linux提供了一种文件访问机制:不经过操作系统内核的缓存,数据直接在磁盘和应用程序地址空间进行传输,该文件访问的机制称作为直接IO,直接IO是一种非常高效的手段
Linux中IO的概念介绍
所有的IO操作都是通过读文件或者写文件来完成的
什么是缓存IO
又被称为标准IO,大多数文件系统的默认IO操作都是缓存IO,Linux中会将IO数据缓存在文件系统的页缓存(page cache),也就是数据会先拷贝到操作系统内核的缓冲区中才会从操作系统内核的缓冲区拷贝到应用程序的地址空间
优点
在一定程度上分离了应用程序空间和实际的物理设备
减少读盘的次数,从而提高性能
同步写、异步写与延迟写
同步写:数据会立刻被写回到磁盘
异步写:在数据完全写到磁盘上的时候会返回给应用程序,不必担心数据丢失
延迟写:定期将放在页缓存中的数据刷到磁盘,不通知应用程序,有数据丢失的风险
缓存IO的缺点
数据在传输过程中需要在应用程序地址空间和页缓存之间进行多次数据拷贝操作,这些操作带来的CPU及内存的开销是非常大的
对于某些特殊的应用程序 ,避开操作系统内核缓冲区而直接在应用程序地址空间和磁盘之间传输数据会比使用操作系统内核缓冲区获取更好的性能,比如自缓存程序
头文件
<sys/types.h>
<sys/stat.h>
<fcntl.h>
打开文件函数open
int open(const char *path,int oflags);
int open(const char *path,int oflags,mode_t mode);
第三个参数设置创建文件的权限
open.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
main(){
int fd;
char *leds = "/dev/leds";
char *test1 = "/bin/test1";
char *test2 = "/bin/test2";
if((fd = open(leds,O_RDWR|O_NOCTTY|O_NDELAY))<0){
printf("open %s failed!\n",leds);
}
printf("\n%s fd is %d\n",leds,fd);
if((fd = open(test1,O_RDWR,0777))<0){
printf("open %s failed!\n",test1);
}
printf("%s fd is %d\n",test1,fd);
if((fd = open(test2,O_RDWR|O_CREAT,0777))<0){
printf("open %s failed!\n",test2);
}
printf("%s fd is %d\n",test2,fd);
}
运行结果
创建函数creat
注意不是create
creat函数可以完全被open函数替代
int creat(const char *pathname,mode_t mode)
创建失败返回-1
creat.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
main()
{
int fd;
//exist file
char *leds="/dev/leds";
//not exist file
char *test1="/bin/test1";
char *test2="/bin/test2";
//file to create
char *test3="/bin/test3";
if((fd=open(leds,O_RDWR|O_NOCTTY|O_NDELAY))<0)
{
printf("open %s failed \n",leds);
}
printf("\n%s fd is %d\n",leds,fd);
if((fd=open(test1,O_RDWR))<0)
{
printf("open %s failed \n",test1);
}
printf("\n%s fd is %d\n",test1,fd);
if((fd=open(test2,O_RDWR|O_CREAT,0777))<0)
{
printf("open %s failed \n",test2);
}
printf("\n%s fd is %d\n",test2,fd);
fd=creat(test3,0777);
if(fd==-1)
{
printf("%s fd is %d\n",test3,fd);
}
else
{
printf("create %s is success\n",test3);
}
}
运行结果
关闭函数close
int close(int fd)
写函数write
在头文件<unistd.h>中
原形为:ssize_t write(int fd,const void *buf,size_t count)
参数*buf,需要写入的数据
参数count,将参数*buf中最多count个字节写入文件中
返回-1写入失败,正常情况返回写入的字节数
write.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
main()
{
int fd;
char *testwrite="/bin/testwrite";
ssize_t length_w;
char buffer_write[]="Hello Write Function!";
if((fd=open(testwrite,O_RDWR|O_CREAT,0777))<0)
{
printf("open %s failed\n",testwrite);
}
length_w=write(fd,buffer_write,strlen(buffer_write));
if(length_w==-1)
{
perror("write");
}
else
{
printf("Write Function OK!\n");
}
close(fd);
}
运行结果
读函数read
ssize_t read(int fd,void *buf,size_t len)
read.c
#include <stdio.h>
#define MAX_SIZE 1000
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
main()
{
int fd;
char *testwrite="/bin/testwrite";
ssize_t length_w,length_r=MAX_SIZE,ret;
char buffer_write[]="Hello Write Function!";
char buffer_read[MAX_SIZE];
if((fd=open(testwrite,O_RDWR|O_CREAT,0777))<0)
{
printf("open %s failed\n",testwrite);
}
length_w=write(fd,buffer_write,strlen(buffer_write));
if(length_w==-1)
{
perror("write");
}
else
{
printf("Write Function OK!\n");
}
close(fd);
if((fd=open(testwrite,O_RDWR|O_CREAT,0777))<0)
{
printf("open %s failed!\n",testwrite);
}
if(ret=read(fd,buffer_read,length_r))
{
perror("read");
}
printf("Files Content is %s \n",buffer_read);
close(fd);
}
运行结果
字符设备控制
字符类LED灯
LED灯的设备节点在/dev目录下
原理图
给KP_COL0和VDD50_EN高电平三极管导通,LED灯会亮
对于IO口(硬件上的IO口)Linux专门设计了一个高效的函数ioctl
在头文件<unistd.h>
int ioctl(int fd,int request,int cmd)
返回值0成功,-1出错
leds.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define LED_NUM 2
#define LED_C 2
//cmd:0 off 1 on
int main(int argc,char *argv[])
{
int fd,led_num,led_c;
char *leds="/dev/leds";
led_num=LED_NUM;
led_c=LED_C;
printf("argv1 is cmd; argv2 is io \n");
if(atoi(argv[1])>=led_c)
{
printf("argv1 is 0 or 1\n");
exit(1);
}
if(atoi(argv[2])>=led_num)
{
printf("argv2 is 0 or 1\n");
exit(1);
}
//use ioctl
if((fd=open(leds,O_RDWR|O_NOCTTY|O_NDELAY))<0)
{
printf("open %s failes\n",leds);
}
else
{
ioctl(fd,atoi(argv[1]),atoi(argv[2]));
printf("ioctl %s success\n",leds);
}
close(fd);
return 0;
}
字符类Buzzer蜂鸣器
原理图
如果MOTOR_PWM为高电平三极管导通,蜂鸣器响,低电平截止不响
buzzer.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#define BUZZER_C 2
//cmd:0 off 1 on
int main(int argc,char *argv[])
{
int fd,buzzer_c;
char *buzzer="/dev/buzzer_ctl";
buzzer_c=BUZZER_C;
printf("argv is cmd \n");
if(atoi(argv[1])>=buzzer_c)
{
printf("argv is 0 or 1\n");
exit(1);
}
//use ioctl
if((fd=open(buzzer,O_RDWR|O_NOCTTY|O_NDELAY))<0)
{
printf("open %s failes\n",buzzer);
}
else
{
ioctl(fd,atoi(argv[1]),atoi(argv[2]));
printf("ioctl %s success\n",buzzer);
}
close(fd);
return 0;
}
运行结果:
字符类ADC模数转换
原理图
XadcAIN0可以读到该点的电压,然后Vadc=R12*VDD1V8_EXT/R13
1.8v对应的是10k欧姆,对应的寄存器数值为0xfff;0v对应的是0欧姆,对应的寄存器数值为0x0
R=r*10000/0xfff
adc.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <termios.h>
#include <string.h>
#include <errno.h>
#include <stdint.h>
int main(int argc,char *argv[])
{
int fd;
char *adc="/dev/adc";
char buffer[512];
int len=0,r=0;
memset(buffer,0,sizeof(buffer));
printf("ADC ready\n");
if((fd=open(adc,O_RDWR|O_NOCTTY|O_NDELAY))<0)
{
printf("open %s failes\n",adc);
}
else
{
printf("open adc success!\n");
len=read(fd,buffer,10);
if(len==10)
{
printf("return null\n");
}
else
{
r=atoi(buffer);
r=(int)(r*10000/4096);
printf("res value is %d\n",r);
}
}
close(fd);
return 0;
}