在Linux系统中,一切都可以看成"文件"<通常包含:普通文件、驱动文件、网络通信文件等等>,系统中所有的操作都可以通过文件I/O实现,因此,掌握文件常用接口很有必要。
0.前言
屏幕前的你如果懂得main函数传递参数机制,可以跳过本部分直接看后续内容。
在正式内容开始前,小编想讲述Linux中main函数传参的机制。
- linux中main函数抢先看:
int main(int argc , char* argv[])
函数参数说明:
- int arc:整型,表示传入参数的个数;
- char* argv[]:字符型数组,表示实际传入的参数,该参数还存在另一种定义方式,即char argv[][];
实际使用样例:
- 样例函数
#include <stdio.h> int main(int argc,char*argv[]) { //打印参数个数 printf("argc:%d\n",argc); //遍历参数并且打印 printf("argv:"); for(int i=0;i<argc;i++) { printf("%s ",argv[i]); } printf("\n"); return 0; }
- 运行结果
- 这里需要注意的是运行命令*./test也是传入main函数的参数**。
1.Linux系统中文件来源
- 来源于磁盘、flash、SD卡等,这些文件是真实存在的,通常以某种格式存储在某个设备上;
- 内核产生的虚拟文件系统,这些文件并不是真实存在的,但是支持真实文件的操作;
- 特殊文件,特殊文件可以是字符设备文件、块设备文件等;
2.访问方式
文件访问函数通常可分为:通用的I/O函数与非通用的I/O访问函数;
- 通用访问I/O的函数
- open:打开文件;
- read:读取文件;
- write:写入文件;
- lseek:定位文件内容;
- close:关闭文件;
- 非通用访问文件I/O的函数:
- ioctl:设备控制接口函数
- mmap:文件映射函数;
查询方式访问
查询方式,设备文件以非阻塞方式打开后,不断发送读取文件请求的一种方式。例如:当你找人有急事时,就会一直尝试联系某人,这就类似于文件的查询方式。
由于查询方式是不断发送读取请求,因此它访问次数非常频繁,占用CPU资源相对较多。
核心代码
#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <signal.h>
int main(int argc, char const *argv[])
{
int fd = 0;
int err = 0;
struct input_id id;
unsigned int evbit[2];
int len,res;
struct input_event event;
//判断参数个数是不是2 一个运行参数 一个文件设备
if (argc != 2)
{
printf("Usage: %s <dev>\n", argv[0]);
return -1;
}
/* 打开驱动程序 */
fd = open(argv[1], O_RDWR | O_NONBLOCK);
//打开失败 返回
if (fd < 0)
{
printf("open %s err\n", argv[1]);
return -1;
}
//不断查询方式 非阻塞
while(1)
{
//读取数据并判断数据大小是否正确
len = read(fd,&event,sizeof(event));
if(len == sizeof(event))
{
printf("get event:type=0x%x code=0x%x value=0x%x\n",event.type,event.code,event.value);
}
else
{
printf("read err\n");
}
}
}
休眠唤醒方式访问
休眠唤醒方式,这里是指:首先文件以阻塞方式打开,当读取到文件数据时,阻塞态就会转换为运行态,读取出数据会直接返回,进行下一步操作。比如:大家在学校上课时(这里将上课作为阻塞态,下课作为运行态),一旦上课铃声响起,咱就会进入阻塞态上课;而下课铃声想起后,大家就开始运行了,进入运行态,直接起飞。🤓🤓🤓
休眠唤醒方式,占用CPU资源相对较少,请求次数相对减少;但是,一旦没有请求到数据就会一直进入阻塞,不会干其他活,这就大大限制了进程的运行状态。
核心代码
#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int fd;
int err;
int len;
int i;
unsigned char byte;
int bit;
unsigned int evbit[2];
struct input_event event;
//判断参数个数是不是2 一个运行参数 一个文件设备
if (argc != 2)
{
printf("Usage: %s <dev> err\n", argv[0]);
return -1;
}
/* 打开驱动程序 */
fd = open(argv[1], O_RDWR);
//打开失败 返回
if (fd < 0)
{
printf("open %s err\n", argv[1]);
return -1;
}
//不断查询 阻塞
while (1)
{
//读取数据 并且判断数据大小是否正确
len = read(fd, &event, sizeof(event));
if (len == sizeof(event))
{
printf("get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value);
}
else
{
printf("read err %d\n", len);
}
}
return 0;
}
以POLL/SELECT方式访问
POLL/SELECT方式访问,首先文件以非阻塞方式打开,再使用poll()函数设置一个固定的超时时间。在这段超时时间内,如果文件能够读出或写入时就会立即返回;否则,时间超时后返回错误。
- 函数原型
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数说明
struct pollfd { int fd; /* file descriptor,目标文件*/ short events; /* requested events,操作事件*/ short revents; /* returned events,超时事件返回值*/ };
nfds_t nfds; /*事件的个数*/
int timeout; /*设置超时时间*/
核心代码
#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <signal.h>
int main(int argc, char const *argv[])
{
int fd = 0;
int err = 0;
unsigned int evbit[2];
int len,res;
struct input_event event2;
struct pollfd fds[1];
nfds_t nfds = 1;
//poll-seelct 设置一个固定的时间查询输入事件
if(argc!=2)
{
printf("err\n");
return 0-1;
}
//以非阻塞的方式打开
fd = open(argv[1],O_RDWR|O_NONBLOCK);
if(fd < 0 )
{
printf("open %s err\n",argv[1]);
return -1;
}
while(1)
{
//设置监听事件相关参数
fds[0].fd = fd;
fds[0].events = POLLIN;
fds[0].revents = 0;
res = poll(fds,nfds,5000);
//返回值有效
if(res > 0)
{
//判断事件方式是否数输入方式
if(fds[0].revents == POLLIN)
{
//数据读取完成后返回
while(read(fd,&event,sizeof(event))==sizeof(event))
{
printf("get event:type=0x%x code=0x%x value=0x%x\n",event.type,event.code,event.value);
}
}
}
else if(res == 0)
{
printf("time out\n");
}
else
{
printf("poll err\n");
}
}
return 0;
}
异步通知方式
异步通知方式,文件是以非阻塞方式打开的,在初始化设置完成后,当没有读取到数据时,程序可以干其他的事,读取数据操作并不影响程序的其他操作。看到这儿,大家是不是觉得这种处理方式很类似于单片机的中断处理函数。😸😸😸
异步通知方式,使用了信号量,信号量在此处的作用是通知驱动程序是否能够成功读取数据,能够成功读取数据后,驱动就读取数据;否则就将其闲置在一旁。
代码
#include <linux/input.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <signal.h>
int fd = 0;
struct input_event event;
void my_sig_handler(int sing)
{
while(read(fd,&event,sizeof(event))==sizeof(event))
{
printf("get event:type=0x%x code=0x%x value=0x%x\n",
event.type,event.code,event.value);
}
}
int main(int argc, char const *argv[])
{
int fd2 = 0;
int err = 0;
struct input_id id;
unsigned int evbit[2];
int len,res;
struct input_event event2;
struct pollfd fds[2];
nfds_t nfds = 2;
unsigned int count = 0,flags;
//判断参数个数是不是2 一个运行参数 一个文件设备
if (argc != 2)
{
printf("Usage: %s <dev>\n", argv[0]);
return -1;
}
/* 注册信号处理函数 */
signal(SIGIO, my_sig_handler);
/* 打开驱动程序 */
fd = open(argv[1], O_RDWR | O_NONBLOCK);
if (fd < 0)
{
printf("open %s err\n", argv[1]);
return -1;
}
/* 把APP的进程号告诉驱动程序 */
fcntl(fd, F_SETOWN, getpid());
/* 使能"异步通知" */
flags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, flags | FASYNC);
while(1)
{
//主进程干的活
printf("count:%d\n",count++);
sleep(2);
}
return 0;
}
3.小结
尽管,上文的四种文件访问方式各有优劣,但只要在合适的时机使用就能够发挥其最大效率!