UNIX/Linux 的一个基本哲学是”一切皆文件”.不仅普通的文件,甚至连各种字符设备、 块设备、套接字等都被当成文件对待,尽管它们的类型差异很大,但 UNIX/Linux 为它们提 供的操作界面却是相同的. Linux 把大部分系统资源当作文件并呈现给用户,用户只需按照文件 I/O 的方式,就能完成数据的输入输出. Linux 文件按其代表的具体对象,可大致分类为:
- 普通文件,即一般意义上的文件、磁盘文件;
- 设备文件,代表的是系统中一个具体的设备;
- 管道文件、 FIFO 文件,一种特殊文件,常用于进程间通信;
- 套接字( socket)文件,主要用在网络通信方面.
文件 I/O 的常用操作方法有”打开”、”关闭”、”读”和”写”等.只要是文件,所有文件都有前面两种方法.系统提供了文件 I/O 的API,以函数的形式提供给应用程序调用.打开文件对应的函数是 open(),读文件对应的函数是 read(),写文件对应的函数是 write(),关闭文件对应的函数是 close().
文件描述符 是进程中用来表示某个文件的一个变量, 文件描述符的作用,类似于在排队取号,业务员(进程)通过叫号(文件描述符)就能找到来搞事的人(文件)
大多数 Raspbian系统中,可通过命令”ulimit -n”查询到这个数值的大小,但注意,不是所有Linux都有这个指令.
文件描述符 0、 1、 2 在Linux上有特殊意义,我们暂时可以不去理解他.只知道他们比较特殊就行了,所以实际上树莓派只有最多7312个同时打开文件.(难道还不够?)
另外文件操作要涉及几个头文件,死记硬背.要用到哪个函数,就用哪个头文件,就是这样的.
#include<sys/types.h> /* 定义数据类型,如 ssize_t, off_t 等 */ #include <fcntl.h> /* 定义 open, creat 等函数原型,创建文件权限的符号常量 S_IRUSR 等 */ #include <unistd.h> /* 定义 read, write, close, lseek 等函数原型 */ #include <errno.h> /* 与全局变量 errno 相关的定义 */ #include <sys/ioctl.h> /* 定义 ioctl 函数原型 */
我们这就上Pi实践下,还是使用VS + Visual GDB神奇组合.就算你不懂Linux各种繁琐指令也能写出不错的程序.如果还不是特别了解,就参考下之前的文章吧.
下面演示的例子,先以可写方式打开当前目录下的hello.txt文件,如果该文件不存在,则创建文件,再往文件中写入一个字符串Hello, welcome to 52pi.net!,把操作结果输出到终端后,关闭文件. 接着再次以只读模式打开该文件,读取刚才写入的数据,并把结果输出到终端最后关闭该文件.
代码:
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <sys/stat.h> int main(int argc, char *argv []) { char sz_str [] = "Hello, welcome to 52pi.net!"; char sz_filename [] = "hello.txt"; int fd = -1; int res = 0; char buf[128] = {0x00}; fd = open(sz_filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); if (fd < 0) { printf("Open file %s failed,errno = %d.\n", sz_filename, errno); return -1; } res = write(fd, sz_str, sizeof(sz_str)); printf("write %d bytes to %s \n", res, sz_filename); fsync(fd); close(fd); fd = open(sz_filename, O_RDONLY); if (fd < 0) { printf("Open file %s failed,errno = %d.\n", sz_filename, errno); return -1; } res = read(fd, buf, sizeof(buf)); buf[res] = '\0'; printf("read %d bytes from file %s ,data = %s \n", res, sz_filename, buf); close(fd); return 0; }
测试结果:
那么刚才代码里面的O_WRONLY都是什么定义呢,我们可以右键,转到定义去查看一下.
当然也可以看我解释:
- O_RDONLY 以只读方式打开文件
- O_WRONLY 以只写方式打开文件
- O_RDWR 以可读写方式打开文件. 上述三种旗标是互斥的, 也就是不可同时使用, 但可与下列的旗标利用OR(|)运算符组合.
- O_CREAT 若欲打开的文件不存在则自动建立该文件.
- O_EXCL 如果O_CREAT 也被设置, 此指令会去检查文件是否存在. 文件若不存在则建立该文件, 否则将导致打开文件错误. 此外, 若O_CREAT 与O_EXCL 同时设置, 并且欲打开的文件为符号连接, 则会打开文件失败.
- O_NOCTTY 如果欲打开的文件为终端机设备时, 则不会将该终端机当成进程控制终端机.
- O_TRUNC 若文件存在并且以可写的方式打开时, 此旗标会令文件长度清为0, 而原来存于该文件的资料也会消失.
- O_APPEND 当读写文件时会从文件尾开始移动, 也就是所写入的数据会以附加的方式加入到文件后面.
- O_NONBLOCK 以不可阻断的方式打开文件, 也就是无论有无数据读取或等待, 都会立即返回进程之中.
- O_NDELAY 同O_NONBLOCK.
- O_SYNC 以同步的方式打开文件.
- O_NOFOLLOW 如果参数pathname 所指的文件为一符号连接, 则会令打开文件失败.
- O_DIRECTORY 如果参数pathname 所指的文件并非为一目录, 则会令打开文件失败.(Linux特有)
而第二项权限意思是这样的:
- S_IRWXU00700 权限, 代表该文件所有者具有可读、可写及可执行的权限.
- S_IRUSR 或S_IREAD, 00400 权限, 代表该文件所有者具有可读取的权限.
- S_IWUSR 或S_IWRITE, 00200 权限, 代表该文件所有者具有可写入的权限.
- S_IXUSR 或S_IEXEC, 00100 权限, 代表该文件所有者具有可执行的权限.
- S_IRWXG 00070 权限, 代表该文件用户组具有可读、可写及可执行的权限.
- S_IRGRP 00040 权限, 代表该文件用户组具有可读的权限.
- S_IWGRP 00020 权限, 代表该文件用户组具有可写入的权限.
- S_IXGRP 00010 权限, 代表该文件用户组具有可执行的权限.
- S_IRWXO 00007 权限, 代表其他用户具有可读、可写及可执行的权限.
- S_IROTH 00004 权限, 代表其他用户具有可读的权限
- S_IWOTH 00002 权限, 代表其他用户具有可写入的权限.
- S_IXOTH 00001 权限, 代表其他用户具有可执行的权限.
如果不指定,权限就是000了,000代表谁都对这个文件没办法.
从上述我们已经了解到文件基本读写,那么现在开始实践,已知Raspbian系统的核心0的主频是存在/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq文件,而CPU温度是存在于/sys/class/thermal/thermal_zone0/temp文件.我们读取这两个文件,定时写入到一个文件里面做记录.
所以,记录工程应该长这个样子.
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #include <sys/stat.h> #include <time.h> int main(int argc, char *argv []) { int cpu_freq_fd = -1; int cpu_temp_fd = -1; int cpu_logger_fd = -1; char freq_buf[128] = {0x00}; char temp_buf[128] = {0x00}; char log_buf[1024] = {0x00}; time_t rawtime; struct tm * timeinfo; cpu_freq_fd = open("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq", O_RDONLY); cpu_temp_fd = open("/sys/class/thermal/thermal_zone0/temp", O_RDONLY); cpu_logger_fd = open("cpu.log", O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); while (1) { lseek(cpu_freq_fd, 0, SEEK_SET); /* 回到文件的开始 */ read(cpu_freq_fd, freq_buf, sizeof(freq_buf)); /* 读取文件 */ lseek(cpu_temp_fd, 0, SEEK_SET); read(cpu_temp_fd, temp_buf, sizeof(temp_buf)); time(&rawtime); timeinfo = localtime(&rawtime); char *now_time = asctime(timeinfo); now_time[strlen(now_time) - 1] = 0; sprintf(log_buf,"%s - CPU = %d MHz - Temp = %d degC\n", now_time, atoi(freq_buf) / 1000, atoi(temp_buf) / 1000); write(cpu_logger_fd, log_buf, sizeof(log_buf)); fsync(cpu_logger_fd); sleep(1); } close(cpu_freq_fd); close(cpu_temp_fd); close(cpu_logger_fd); return 0; }
执行效果,是不是很棒,这样就有一个监控程序了.通过程序不难看出,就是不断写cpu.log文件.然后休眠.
此处用到的两个工程:
不错不错,学习一个
就是一个简单的读写/proc节点获取系统信息,说的那么高级。。