7、数码相框之修改电子书源码支持远程打印


电子书涉及知识点:
字符编码
点阵显示
矢量字体显示
模块化编程
多线程编程

代码见:第 1 个项目数码相框全部源码_图片_文档\源码(含讲课过程中即时编写的文档)\10.远程打印\source\12.show_file_input_netprint

1、功能分析

要实现的功能:
在这里插入图片描述
第4点的理解:如果用stdou.c来打印的话肯定很快,但是用netprint来打印的话还会涉及客户端和服务端,不会一开始马上就打印出来。所以要先把数据存入buffer,当客户端连接之后再传输数据。
第5点理解:参照内核printk的实现的功能,实现设置和显示打印级别。

2、程序框架

在这里插入图片描述
定义结构体成员的时候之所以用DebugPrint(const *format,***)这是因为参照printf的函数实现是这样的,可以通过man 3 printf来查看。

3、修改程序

同之前一样,这里也是相当于新增一个模块,然后通过注册一个结构体来完成,先考虑这个结构体的成员有哪些。

要实现既可以通过串口打印出调试信息也要通过网络传输打印出调试信息,由于有多个方式,我们还要根据客户端选择打印的方式,肯定要有个名字: name

  • 那么首先肯定还是要初始化成员:DebugInit
  • 相对于的要有个退出的函数DebugExit
  • 我们要通过他们来实现打印,肯定有个打印的函数: DebugPrint
  • 我们要实现选择使用哪种或是否同时使用多个打印的方式,要有一个标准来表示该打印方式是否被使用: isCanUse

3.1、debug_manager.h

有了上面的思路先把相应的文件添加好,在include里面添加一个.h,定义上面说到的这些成员的结构体。
在这里插入图片描述

3.2、debug_manager.c

参照别的manager.c,修改名字即可。

3.3、stdout.c

1、分配设置一个结构体:
在这里插入图片描述
对于标准输入和标准输出我们不需要做什么initexit,一开始我们isCanUse都设置为1,让他们一开始都能使用,要注意这个结构体还有其他的成员没有初始化,所以我们在别的地方调用的时候要先判断结构体里面有没有相关成员。

下面来分别实现上面的这些函数
2、实现相关的函数成员:
StdoutDebugPrint这个函数的返回值应该写成什么?

我们可以参照printf函数看他的返回值,man 3 printf,发现它的返回值为返回已经打印的个数,但是错误返回好像没找到,我们这里就直接返回0就好了。

我们之前定义这个标准打印函数的时候的参数一开始是设置为变长的,是参照printf的函数模型,在man 3 printf中查看。
在这里插入图片描述
由于我们这里StdoutDebugPrint函数的参数。那么这参数怎么处理呢?我们参照内核里面的printk是怎么做的。
在这里插入图片描述
上面的内核printk传入的也是变参,他就是用上面的方式处理变参的,我们参照内核的printk我们修改StdoutDebugPrint为:
在这里插入图片描述
我们只要把内核中的vprintk换成printf就可以了,其他的语句就是处理变参的改完之后我们发现,不是说我们的printf是变参么,为什么我们上面直接替换之后不是变参了呢?可能不是这样做的,我们参照下glibc里面printf的实现(glibc可以在网上下,然后用sourceinsight打开),所有的库函数都可以在glibc里面找到他的实现方式。

glibc里面printf的实现:
在这里插入图片描述
发现glibc里面是用vfprintf来实现的,我们把之前的修改的替换成:
在这里插入图片描述
其实上面的框中的内容就是printf最终的代码我们把变参放到了上面一层,变参的原理还是上面的讲的,最终变成了如下:
在这里插入图片描述
由于我们是标准输入,我们直接用printf打印出来就可以了。

3.4、netprint.c

先明确几个问题。

首先我们要确定我们这里采用TCP还是UDP。开发板是作为server端还是client端,我们这里采用网络通信使用 UDP,至于把烧入开发板的程序当做 server 端还是 client 端由你自己来决定。

这里把烧入开发板的程序当做 server 端,因为开发板上你可以随便执行应用程序,然后你想在哪一台机器上打印,就用那台机器登录开发板就可以了。

结构体变量定义如下:
在这里插入图片描述

NetDbgInit函数

其实里面要完成的就是UDPserver端socketbind函数。
在这里插入图片描述
由于我们要实现向客户端发送数据,但是数据可能已经准备好了,但是客户端还没连接成功,所以我们必须先将数据放入一个缓冲中,等待客户端连接成功才能发送,由于我们缓冲区的数据要一边产生一边消耗,所以这样我们就引入了环形缓冲区。由于我们想通过一个环形缓冲区来实现一个存储数据的池子,而环形缓冲区有连个互不干扰的指针,读和写,而且创建之前必须分配一个环形缓冲区的内存,另外我们还要实现从客户端接收命令然后发送指定的数据,所以我们可以分别创建两个线程,一个用来读,一个用来。
在这里插入图片描述
写进程是用来向客户端发送数据的,所以必须等待客户端连接成功才能发送,那么什么时候客户端连接成功了呢?

只有做个全局变量,然后在接收线程的时候收到了数据,我们就把这个标志未置1,所以发送线程一开始为休眠,只有当为连接成功并且环形缓冲区有的时候就让写进程发送数据,那么写进程是何时被唤醒呢?

只有当我们应用层调用netprint的时候把数据放入环形缓冲区之后就唤醒写进程
那么何时发送缓冲区的数据呢?
只有唤醒了并且缓冲区有数据才发送。

Q1:能不能在客户端连接成功的时候唤醒呢?而不是在应用层调用netprint的时候把数据写入缓冲区才唤醒;
Q2:能不能在判断缓冲区是否不为空的时候唤醒呢?和在应用层调用netprint的时候把数据写入缓冲区才唤醒有区别么;
Q3:我们为什么要创建两个线程,我们不是还有个主线程么,不能让主线程来接收数据么?写两个线程有什么好处呢?

所以上面的代码修改如下:
在这里插入图片描述

NetDbgPrint函数

同理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

「已注销」

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值