1 main的输入参数,并且在命令行运行文件的时候输入


我的练习

先写了个单纯输出的hello

按照教程里那样写hello

所以gcc编译过程应该是

  1. 先创建一个.c文件
  2. gcc -c -o hello.o hello.c 在当前文件夹生成一个.o文件
  3. gcc -o hello hello.o 在当前文件夹生成一个可执行文件
  4. ./hello 执行文件,后面可以带输入参数,用空格区分。而且这个文件名本身算一个参数,放在argv[0]

2 gcc编译器

Linux里不是用keil也不是用vs 而是用gcc 在命令行里编译文件。


【这是第三课第2集的图


预处理就是把宏定义替换进代码,把头文件读入。
汇编的时候是把代码转换为汇编程序,这时候如果程序有误会报错。


上图是同时把两个文件作为同一个项目进行编译,生成一个test工程文件

但是如果要修改main.c的话,再执行这条命令,sub也不得不重新编译一次。


按上图先生成.o文件【机器码】后再链接
sub就不必费时间重新编译,但第三句那里还是要写一遍进行链接。

如果文件太多,不知道改了哪些.c文件

看文件时间,如果.o文件比.c文件时间早,就得重新生成.o文件,然后再重新链接生成test文件

./test
就能运行了



末尾加一个-v能看到系统会去哪些路径找头文件里的代码文件


下图是作者把自己的头文件用了尖括号
然后用-I命令把当前的文件夹路径放进了尖括号头文件的搜索路径的列表里【看这效果是一次性的,如果下一次gcc编译不加-I命令的话,程序仍然不会去找 ./ 这个路径


这句是把sub这个子函数的文件压缩成静态库
然后再跟main链接到一起


.so是动态库,动态库的文件大小会比静态库小一些

动态库文件要指定读取路径,不然会显示找不到文件

3 makefile

说是不用学得太仔细,但如果非要仔细学makefile的话看这里

【注意,在makefile里的tab键是重要格式,tab键后必须跟命令,如果光有tab键就换行了 程序会出错,如果命令前是空格而不是tab则无法识别到命令】

makefile跟.c .h一样是一个某种格式的代码文件
它看起来是帮忙执行gcc编译的批处理文件?
在编写的时候有固定语法

如果a.o 或b.o更新了,就执行gcc链接生成test

如果a.c更新了,就执行 gcc -c 重新编译生成a.o文件
b.o也是


如果用vi改了a.c 文件,make文件就会自动gcc -c一下a文件,报了个警告 没事,然后重新gcc一下test文件

通配符

为了不要a.c b.c c.c 挨个写,所以用各种符号减少重复

下图是替换后的样子

加了一个c.c文件放了一个新的子函数。对比一下差别。

指令


多加了一个指令,输入clean的时候就执行那句rm操作
.PHONY:是为了声明clean 跟test那个不一样 并不是一个文件。

变量


【加@是为了在运行时不显示echo这个指令本身


怪了。对延时变量来说,C无论是在前文赋值还是在后文赋值都是先于命令的。
而对即时变量却直接就是赋值时还未定义的为空值的C。

make时设定变量数值



嗯,输入的定义是优先于?=的赋值的,这个好理解。

函数

foreach

foreach是 把list里的所有变量用var取出来 然后挨个执行test这句内容

make显示 B = a.o b.o c.o

filter


%是通配符,在这里代替字母

wildcard

pattern的位置是我们打算找的一些文件。在make时程序会在当前目录里找有没有符合要求的文件

输出是 files3 = a.c b.c c.c 【因为当前路径里只有这3个文件符合要求

patsubst

综合运用

阿巴阿巴。。。
编程没天赋的人可能就是这种感觉了?
我当时学c的时候明明理解得挺快的啊。。。。
Linux Makefile 生成 *.d 依赖文件以及 gcc -M -MF -MP 等相关选项说明

4 文件io

【这视频内容有点离题啊
写代码的时候
用man指令可以查函数的用法【如printf和read】,man有好几本手册可以查函数,不同手册里可能有同名函数,可以指定手册【printf在3号手册
$ man 3 printf】 可以查函数的头文件和输入参数,在man里 f和b是翻页,q是退出。


说是代码在调用open或者read的时候,用户区会向内核区抛出异常,并且这个异常附带着参数 以指向函数所对应的操作

7 输入系统应用编程

01_输入系统框架及调试


核心层负责把收到的各类数据进行分类统一处理


每个设备输入的数据类型不一样多,所以都要发一个同步信号表示自己当前发完了一批数据。


上图是触摸屏传来的数据。方框内的是同步信号。。【。我看着是按时间来分的啊?跟数据的完整性有关吗??】

这句指令可以知道每个设备节点的信息

I : 总线 厂家 产品 版本号
N : char *name
P : 物理上的名字,这里写的是触摸屏 char *phys
H :这个设备是event0
B : EV对应的是下图代码里的evbit 支持的事件event的置位情况。数值是b代表的是二进制1011 有3个数据位置1,也就是0号,1号,3号数据需要传输,那就是上上上图宏定义里对Event type里列出来的EV_SYN ,EV_KEY和EV_ABS这三个数据。
B : ABS是绝对位移相关信息code的置位情况


这里的信息和内核里的信息是一一对应的

下图的code部分就是对应的宏定义的编号【所以为什么会有39啊。上面说置位了35 36 没说39啊
type为1代表的是按键类事件,code代表的是第014a号键 value代表具体的数值是0001 表示按下

我的练习

看一下现在系统上有哪些设备,我看到了触摸屏和我的蓝牙鼠标

cat /proc/bus/input/devices


看一下我的鼠标都发些什么信息,系统提示我权限不够,所以我加了sudo 【为后文的失败埋下伏笔

sudo hexdump /dev/input/event5


00263f0 之前都是移动,然后我按下并松开左键进行截屏,不过不知道为什么没有全零同步帧,之前移动并停下的时候也时常没有同步帧出现在最后。
感觉现在的格式应该跟教程里的不一样了,反正【微秒】那栏肯定不对。

02_现场编程读取获取输入设备信息

写程序读取内核里的设备数据

目标是:只要运行01_get…这个可执行文件,并输入设备路径 就能读取到对应信息。

在这段内核代码里cmd有EVIOCGID这个分类,这里有设备id这个信息被保存在input_id这个结构体里
这个结构体具体有下列信息

我们的程序通过一个包装好的函数去获取信息,只需要设备路径和所需内容的对应置位信息
因此,我们只需要新建一个struct input_id thisId;
写一句
fd=open(…)
err = ioctl(fd,EVIOCGID,&thisId);
就可以把内核里的EVIOCGID这个case里的信息存到thisId结构体变量里去。

但是并不是所有信息都能简单靠EVIOCGID这样一个宏就能去找到的,而是需要一个帮忙转换格式用的函数,EVIOCGBIT()函数。
下图就是对应置位信息所代表的含义,而我们不需要去记,用函数就好了。【韦东山说 具体什么时候用宏定义,什么时候用EVIOCGBIT()函数,得具体看内核代码来决定】

下图就是放在request这个位置的函数

【把0改成1 就能得到keybit的信息

我们写的读取信息的代码长这样

unsigned int evbit[2] 是因为内核里的evbit是 unsigned long

那个头文件,我们包含的不是内核的input头文件,而是交叉编译的工具链里的头文件。

不是很明白他是怎么找的,但总之是包含

然后打开文件 open()函数可以用 $ man 2 open 查看用法
1 看所需头文件
2 输入参数格式

为了显示内核里的event的这些对应的宏信息。
代码里写了一个字符串数组保存了宏信息,并在读取了设备数据之后对应显示。【我总是想把这些宏的16进制数换成2进制数啊。但这里的用法真的不是这样。这里的宏的数值是代表设备里数据的第几bit。

evbit 是uint类型的数组,sizeof(evbit )为8字节 。总共有len个数据,用uchar 类型的byte拆成8份挨个取出,每个数据有8bit,如果数据中的某个位为1,则显示对应那一组的对应那个位的ev数据.
所以,event types那些宏就是第x个位置1使能的意思咯?

我的练习

  1. 设备权限

第二次的打开失败估计就是因为权限问题。
但是我甚至找不到我的蓝牙鼠标的文件位置,改不了权限【顺便一提,我打开can通信的那个代码也不算输入设备,那个在Linux看来应该是SPI通信】

直接修改设备文件权限没有意义,设备拔插时,udev 会更新 /dev,并且每次设备插入的时候,Bus 和 Device 的值都不确定。
如何修改 Linux 设备访问权限

笑死,我没改权限 我直接 变身root

$ su root

  1. 头文件

    所需头文件就是韦东山从Linux内核里找的头文件 <linux/input.h>

  2. OK了。【我确实需要makefile文件。编译麻烦死了 】

03_四种方式读取输入数据

有4种方式读取设备信息


不等和死等模式 【open()输入参数里的nonblock标志位


上述两个方法就是open函数的一个输入参数的区别罢了。

那么,在运行代码文件的时候,除了输入必须的<设备路径>之外,还可选输入noblock这个输入

在代码里用strcmp函数将输入参数进行对比,然后执行对应的open方式


【淦,我没注意到strcmp前面还有个取反! 这个字符串比较的函数是在字符串相等时返回0】
fd指针是当时open()函数打开的event0这个设备,在这里是触摸屏
event这个结构体指针是从内核代码里知道的。我们调用read的时候,系统会对应唤醒evdev_read()这个函数,系统会把设备数据塞进__user *buffer里,函数内部代码会具体用到input_event这个结构体类型【但我看不懂,所以就不截图了。】总之,知道了数据保存的形式之后就能按照sizeof来读取数据。
如果用了O_NONBLOCK这个标志,那在程序运行的时候就会不断输出else里的语句。
如果没用那个标志,就会一直等着触摸屏输入【就是卡在了read()这函数里

我的练习

确认了阻塞方式就是卡在了read()函数内部。明明是读取函数进行阻塞,但标志位却是在open函数里设置。看内核代码,是检查fd句柄里的f_flags的对应bit的标志位决定是不是直接return的。


等一会模式

这个方法的做法是 在read函数之前加一句poll函数。
poll函数可以同时监视好几个文件的IO情况,所以第一个参数是一个结构体指针数组。结构体的组成看下图的底部。
本例只监视一个文件,所以只创建struct pollfd fds[1]。这个结构体需要在传入函数之前赋值。同时这个结构体里除了设备的fd之外【诶,才发现fd是int,怎么不是指针,难道是索引节点的inode吗】还需要设置事件events,具体选择可以看上图的表格,我们选用的是有数据可读的情况。第三个结构体是函数自己写的,内容也是表格里的那些事件类型。events是我们期望去了解的事件,revents是当函数做完事情之后,它会记录一下自己刚才完成的是哪类事件。
为什么不把revents搞成返回值呢?因为返回值要返回的是有多少个文件IO有事了。在我们这个例子里,返回值最多只能是1,如果是0就是没消息而且等待超时了,返回-1就是出错了。
话说回来,poll还有第二个参数,是对应的文件数量
第三个参数,限时时间,毫秒。本文设置3000ms

那么,在程序运行时,poll函数如果没读到内容,会等3秒,这时候是堵在poll这里的,中间有信息的话随时能放行给read,如果等3秒还没数据,就会返回超时,然后让程序跑一圈,重新回到poll的时候再等3秒。


select和epoll的区别

select的时间复杂度O(n)。它仅仅知道了,有I/O事件发生了,却并不知道是哪那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。所以select具有O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。 epoll的时间复杂度O(1)。epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的。(复杂度降低到了O(1))
poll函数详解
linux select函数详解

我的练习

要注意输入数据类型,我差点把poll的第一个参数直接用了之前的fd句柄。。但是第二个参数直接写1能用【当然最好还是憋那么干】
另外,我加了个计时的函数,基本上不太准,而且鼠标在动的时候计时数据经常错,尽量不要用。


有事喊我模式


像qt那样注册一个回调函数。只需要信号类型和对应的函数 这俩绑定到一块去。这样只要来了信号就知道该干啥了。
然后,那个读取设备的进程有一个进程ID,叫PID。进程是内核系统里的事,要获取到这个PID之后发给设备驱动,告诉驱动:如果你有数据来了,发个信号,我让这个进程给你办事。
主函数的while循环里没有读取设备内容的函数。但是事先要记得打开信号接收的标志位。
然后就可以打开设备了open()

  1. 注册信号函数
    sighandler_t 是函数类型。
    这个函数类型输入一个int 无返回值



这句写在main函数里

这个是子函数

  1. 打开驱动程序
    这一步和以前一样的
  2. 告知进程号
    教程没说中间的宏是干啥用的。。
  3. 设置flag
    这里也没细讲

    但是搞完这些就完成了,后面可以写自己的while(1)里的随便什么任务了,设备如果有消息就会通过回调那个注册的信号处理函数进行处理。

总结一下全程:

Linux进程间通信详解(六) 信号种类及函数

我的练习

man signal 里没有提到SIGIO这个宏,但是编辑器自动补全里面有,纳闷。
教程里用while,但是我寻思着 只要有IO信号就会进回调函数,应该用if也一样。
有一些宏跟教程对不上。
没有进入回调函数,开始找bug
看看fcntl()的资料
   fcntl()针对(文件)描述符提供控制.
   参数fd是被参数cmd操作(如下面的描述)的描述符.
   针对cmd的值,fcntl能够接受第三个参数(arg)
fcntl函数有5种功能:
1.复制一个现有的描述符(cmd=F_DUPFD).
2.获得/设置文件描述符标记(cmd=F_GETFD或F_SETFD).
3.获得/设置文件状态标记(cmd=F_GETFL或F_SETFL).
4.获得/设置异步I/O所有权(cmd=F_GETOWN或F_SETOWN).
5.获得/设置记录锁(cmd=F_GETLK,F_SETLK或F_SETLKW).

O_ASYNC 当I/O可用的时候,允许SIGIO信号发送到进程组,例如:当有数据可以读的时候

fcntl的返回值: 与命令有关。如果出错,所有命令都返回-1,如果成功则返回某个其他值。下列三个命令有特定返回值:F_DUPFD,F_GETFD,F_GETFL以及F_GETOWN。第一个返回新的文件描述符,第二个返回相应标志,最后一个返回一个正的进程ID或负的进程组ID。

Linux fcntl函数详解

//能成功运行的宏signal(SIGIO, sig_func);fcntl(fd,__F_SETOWN,getpid());flags = fcntl(fd , F_GETFL);fcntl(fd,F_SETFL,flags|O_ASYNC);

话说,进了信号回调函数之后再回到while循环,sleep(2)就不管用了。【是睡不着了么?/狗头】

我的练习代码

韦东山 嵌入式Linux应用开发基础知识 上【gcc makefile 输入设备相关推荐

  1. 韦东山 嵌入式Linux应用开发基础知识 下【串口 IIC SPI

    10 串口 因为UART没有时钟信号,无法控制何时发送数据,也无法保证双方按照完全相同的速度接收数据.因此,UART为每个字节添加额外的起始位和停止位,以帮助接收器在数据到达时进行同步: 双方还必须事 ...

  2. 002_韦东山嵌入式Linux应用开发基础_实操碰到的问题集锦

    嵌入式Linux应用开发基础_韦东山教程思考笔记 配合<嵌入式Linux应用开发完全手册V5.1_IMX6ULL_Pro开发板> 文件目录 访问根/目录下,Filesystem Root目 ...

  3. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之Pinctrl子系统和GPIO子系统的使用

    文章目录 前言 1.Pinctrl子系统 1.1.为什么有Pinctrl子系统 1.2.重要的概念 1.3.代码中怎么引用pinctrl 2.GPIO子系统 2.1.为什么有GPIO子系统 2.2.在 ...

  4. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之按键驱动框架

    文章目录 前言 1.APP怎么读取按键值 1.1.查询方式 1.2.休眠-唤醒方式 1.3.poll方式 1.3.异步通知方式 1.5. 驱动程序提供能力,不提供策略 2.按键驱动程序框架--查询方式 ...

  5. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之LED模板驱动程序的改造:设备树

    文章目录 前言 1.驱动的三种编写方法 2.怎么使用设备树写驱动程序 2.1.设备树节点要与platform_driver能匹配 2.2.修改platform_driver的源码 3.实验和调试技巧 ...

  6. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之设备树模型

    文章目录 前言 1.设备树的作用 2.设备树的语法 2.1.设备树的逻辑图和dts文件.dtb文件 2.1.1.1Devicetree格式 1DTS文件的格式 node的格式 properties的格 ...

  7. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之总线设备驱动模型

    文章目录 前言 1.驱动编写的三种方法 1.1.传统写法 1.2.总线驱动模型 1.3.设备树驱动模型 2.Linux实现分离:Bus/Dev/Drv模型 2.1.Bus/Dev/Drv模型 2.2. ...

  8. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之驱动设计的思想:面向对象/分层/分离

    文章目录 前言 1.分离设计 驱动程序分析---程序分层 通用驱动程序---面向对象 个性化驱动程序---分离 APP 程序分析 前言 韦东山嵌入式Linux驱动开发基础知识学习笔记 文章中大多内容来 ...

  9. 【嵌入式Linux】嵌入式Linux驱动开发基础知识之LED驱动框架--面向对象、分层设计思想

    文章目录 前言 1.LED驱动程序框架 1.1.对于LED驱动,我们想要什么样的接口? 1.2.LED驱动要怎么写,才能支持多个板子?分层写 1.3.程序分析 驱动程序 应用程序 Makefile 1 ...

最新文章

  1. Rocksdb Iterator实现:从DBIter 到 TwoLevelIter 的漫长链路
  2. Ztree节点增加删除修改和Icheck的用法
  3. GitLab10安装-部署-汉化-备份-升级
  4. 白话Elasticsearch58-数据建模实战_基于nested object实现博客与评论嵌套关系
  5. Java并发编程(2):线程中断(含代码)
  6. 怎么画正五边形步骤_悄悄告诉你们:一种没有美术基础,也能画好画的方法
  7. 【数据结构】哈夫曼树与哈夫曼编码
  8. Photoshop的批处理(有海量的数码照片,想传上网的话就看下)
  9. 使用Apriori进行关联分析(一)
  10. 设计灵感|引导页设计中如何借助图形来展现场景?
  11. beego使用php,使用GO语言Beego框架开发一个简易留言板
  12. 用 WebSocket 实现一个简单的客服聊天系统
  13. IntelliJ IDEA 中文语言包插件
  14. 华硕服务器主板装系统,装机高手教你华硕主板bios设置图解
  15. 我们为什么要使用空接口
  16. PC微信聊天记录备份恢复测试
  17. 懒汉延迟加载设计模式反射注解
  18. [NAS] Synology (群晖) DSM7.0 使用自定义供应商DDNS
  19. RHEL5配置Samba服务器实现文件共享
  20. python代码什么时候用单引号_Python 中的单引号和双引号有什么区别?

热门文章

  1. 配置vscode终端字体
  2. 7-3 sdut-顺序结构-1 利用海伦公式求三角形面积,了解世界科学史
  3. 获取手机的设备信息和唯一ID
  4. 用计算机程序实现离散化的对象模型,模糊PID应用
  5. 先进PID控制Matlab仿真第4版-pdf课本+仿真程序
  6. 几个 Linux 耍酷命令,手把手教你如何技术撩妹!技术脱单
  7. Pyqt qtableview 大量动态刷新数据
  8. TMC4671-LA完全集成伺服控制器电机驱动芯片带FOC控制
  9. mt4查看虚拟服务器,mt4 如何查看服务器地址
  10. 伽卡他卡如何关闭,并恢复任务管理器