Linux设备驱动开发入门之——hello驱动

2023-10-26

1. Linux驱动程序的分类

Linux 中主要分为三大类驱动:字符设备驱动、块设备驱动和网络设备驱动。

1、字符设备驱动:因为软件操作设备是是以字节为单位进行的,是按照字节流进行读写操作的一种设备。典型的如LCD、蜂鸣器、SPI、触摸屏等驱动,都属于字符设备驱动的范畴。大部分的驱动程序都是属于字符设备驱动。

2、块设备驱动:块设备驱动是相对于字符设备驱动而定义的,因为块设备被软件操作时,是以块为单位进行操作的(块指的是多个字节组成一个块)。块设备大多指的都是各种存储类类设备,比如EMMC、SD卡、NANDFlash、U盘等等。

3、网络设备驱动:专门针对网络设备而设计的一种驱动,不管是有线还是无线网络,都属于网络设备驱动。

另外,一个设备可以属于多种设备驱动类型,比如 USB WIFI设备,其使用 USB 接口,所以属于字符设备,但是其又能上网,所以也属于网络设备驱动。

2. 与Linux驱动开发相关的介绍

1、Linux下的应用程序是如何调用驱动程序的

应用程序在使用C库函数所提供的 open/read/write 等等函数时,最终会进入到内核里面,调用内核所提供的 sys_open/sys_read/sys_write 等等函数。而此时如果内核发现应用程序需要访问的是驱动的话,那么就会调用该驱动程序所提供的 drv_open/drv_read/drv_write 等函数;如果发现应用程序访问的不过是普通文件的话,那么内核就会调用访问普通文件的那套函数。下图形象的给出了调用关系:
在这里插入图片描述
驱动程序实际上起到承上启下的作用,上承应用程序,对下则实现了具体的硬件操作。

2、Linux驱动程序的两种运行方式

  • 可以把驱动程序编译进内核里面,这样内核启动后就会自动运行驱动程序了;
  • 将驱动程序编译成以.ko为后缀模块文件,然后在Linux启动后,我们自己手动安装驱动程序。一般来说,这种方式在开发驱动阶段常用。

3、Linux驱动开发中常用的几个命令

  • insmod(install module):用于安装以Linux的驱动模块
  • rmmod(remove module):卸载驱动模块
  • lsmod(list module):打印出当前内核中已经安装的模块
  • modinfo(module information):打印出某个 xxx.ko 文件的模块信息。用法:modinfo xxx.ko

3. Linux驱动开发需要准备的工作

1、已经安装好的交叉编译工具链

​ 我们开发的驱动程序是要运行在ARM架构上的,所以需要准备好ARM架构的编译工具链。一般来说我们使用开发板厂商,或者SoC原厂提供交叉编译工具链即可。具体如何安装交叉编译工具链这里不多啰嗦了。

2、准备已经配置和编译好你对应板子的内核源码

​ 驱动程序是运行在内核空间的,不同于应用程序运行在用户空间。驱动程序已经是属于内核的一部分了,而编译驱动程序需要借助于内核源码来编译。

​ 另外,内核源码的版本一定要和你板子上实际运行的版本相一致,否则编译出来的驱动程序会因为版本不同而无法在你的板子上运行。

3、你的开发板接线正常,网络正常(要保证开发板和ubuntu之间可以相互ping通,因为我们通过nfs方式把ubuntu编译好的 xxx.ko 等文件传输到开发板)。

4. show出你的代码

4.1 hello驱动的编写

我们在编写应用程序的时候,首先也是先学会如何再电脑屏幕上输出 “helllo world”。同样的,我们编写的第一个驱动程序,也是先学会hello驱动,该驱动不涉及任何的硬件操作,而且也是属于字符设备驱动的范畴。主要实现的功能是:

1、应用程序调用 open、read、write 等函数时,对应的驱动函数都打印出内核信息;

2、应用程序调用 write 函数时,传入的数据保存在驱动中;

3、应用程序调用 read 函数时,把驱动中保存的数据再返回给应用程序,并打印出来

驱动程序的编写其实也是有迹可循的,主要的编写步骤如下:

  1. 确定主设备号,也可以让内核自动分配
  2. 定义自己的 file_operations 结构体
  3. 实现对应的 drv_open/drv_read/drv_write 等函数,填入 file_operations 结构体
  4. 把 file_operations 结构体告诉内核:register_chrdev
  5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数
  6. 有入口函数就应该有出口函数:卸载驱动程序时,出口函数调用 unregister_chrdev
  7. 其他完善:提供设备信息,自动创建设备节点:class_create, device_create

其中,驱动程序核心中的核心就是 file_operations 这个结构体了。在这个结构体里面,就是要实现这个驱动程序自己的 open、read、write等函数,并通过Linux内核提供的接口注册到内核里面去。而其他的一些步骤都是为了遵循LInux驱动程序的编写规范,用于完善这个驱动程序的。

驱动代码的编写也不用完全都自己写,我们可以参考Linux内核提供的一些已有的驱动程序,下面我们就参考内核的一份 misc 驱动,编写我们自己的hello驱动程序,hello驱动代码如下:

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/stat.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/kmod.h>
#include <linux/gfp.h>

/* 1. 确定主设备号                                                                 */
static int major = 0;
static char kernel_buf[1024];
static struct class *hello_class;

#define MIN(a, b) (a < b ? a : b)

/* 3. 实现对应的open/read/write等函数,填入file_operations结构体                   */

/*
 * @description		: 从设备读取数据
 * @param - file	: 内核中的文件描述符
 * @param - buf		: 要存储读取的数据缓冲区(就是用户空间的内存地址)
 * @param - size	: 要读取的长度
 * @param - offset	: 相对于文件首地址的偏移量(一般读取信息后,指针都会偏移读取信息的长度)
 * @return 			: 返回读取的字节数,如果读取失败则返回-1
 */
static ssize_t hello_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
	int err;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = copy_to_user(buf, kernel_buf, MIN(1024, size));
	return MIN(1024, size);
}

/*
 * @description		: 向设备写数据
 * @param - file	: 内核中的文件描述符
 * @param - buf		: 要写给设备驱动的数据缓冲区
 * @param - size	: 要写入的长度
 * @param - offset	: 相对于文件首地址的偏移量
 * @return 			: 返回写入的字节数,如果写入失败则返回-1
 */
static ssize_t hello_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	int err;
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	err = copy_from_user(kernel_buf, buf, MIN(1024, size));
	return MIN(1024, size);
}

/*
 * @description		: 打开设备
 * @param - node	: 设备节点
 * @param - file	: 文件描述符
 * @return 			: 打开成功返回0,失败返回-1
 */
static int hello_drv_open (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

/*
 * @description		: 关闭设备
 * @param - node	: 设备节点
 * @param - file	: 文件描述符
 * @return 			: 关闭成功返回0,失败返回-1
 */
static int hello_drv_close (struct inode *node, struct file *file)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	return 0;
}

/* 2. 定义自己的file_operations结构体                                              */
static struct file_operations hello_drv = {
	.owner	 = THIS_MODULE,
	.open    = hello_drv_open,
	.read    = hello_drv_read,
	.write   = hello_drv_write,
	.release = hello_drv_close,
};

/* 4. 把file_operations结构体告诉内核:注册驱动程序                                */
/* 5. 谁来注册驱动程序啊?得有一个入口函数:安装驱动程序时,就会去调用这个入口函数 		*/
static int __init hello_init(void)
{
	int err;
	
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	major = register_chrdev(0, "hello", &hello_drv);  /* /dev/hello */

	/* 7. 其他完善:提供设备信息,自动创建设备节点                                 */
	hello_class = class_create(THIS_MODULE, "hello_class");
	err = PTR_ERR(hello_class);
	if (IS_ERR(hello_class)) {
		printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
		unregister_chrdev(major, "hello");
		return -1;
	}	
	device_create(hello_class, NULL, MKDEV(major, 0), NULL, "hello"); /* /dev/hello */
	
	return 0;
}

/* 6. 有入口函数就应该有出口函数:卸载驱动程序时,就会去调用这个出口函数           */
static void __exit hello_exit(void)
{
	printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
	device_destroy(hello_class, MKDEV(major, 0));
	class_destroy(hello_class);
	unregister_chrdev(major, "hello");
}

/* 指定驱动的入口和出口,以及声明自己的驱动遵循GPL协议(不声明的话无法把驱动加载进内核) */
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

hello驱动程序的几点说明:

  1. 驱动程序要打印信息的时候,调用的函数是 printk ,而应用程序调用的是 printf

  2. 应用程序和驱动程序之间传递数据,不能使用简单的赋值或者memcpy等,要使用内核提供的 copy_from_user/copy_to_user 函数。当然如果需要传递大量数据的时候,还可以使用内存映射的方式

  3. 阅读一个驱动程序,首先要找到驱动程序的入口函数。上面的驱动入口函数就是hello_init函数,该函数做的事情就是向内核注册了一个 file_oprations 结构体,并且完成自动创建设备节点相关的代码

  4. file_oprations 结构体是驱动程序的核心,里面提供了本驱动 open/read/write/release 等成员,当应用程序调用了open/read/write/release 等函数时,就会导致对应驱动的这些成员函数被调用

4.2 编写hello应用程序测试

下面我们编写一个hello应用程序来测试我们编写好的驱动程序。该应用程序要实现的功能是:

  1. 向驱动程序写入一串字符串
  2. 把驱动程序保存起来的字符串读出来

hello_drv_test 应用程序代码如下:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

/* 该应用程序用法:
 * ./hello_drv_test -w abc	向hello驱动写入字符串 abc 
 * ./hello_drv_test -r		读取驱动程序中保存的数据
 */
int main(int argc, char **argv)
{
	int fd;
	char buf[1024];
	int len;
	
	/* 1. 判断参数 */
	if (argc < 2) 
	{
		printf("Usage: %s -w <string>\n", argv[0]);
		printf("       %s -r\n", argv[0]);
		return -1;
	}

	/* 2. 打开文件 */
	fd = open("/dev/hello", O_RDWR);
	if (fd == -1)
	{
		printf("can not open file /dev/hello\n");
		return -1;
	}

	/* 3. 写文件或读文件 */
	if ((0 == strcmp(argv[1], "-w")) && (argc == 3))
	{
		len = strlen(argv[2]) + 1;
		len = len < 1024 ? len : 1024;
		write(fd, argv[2], len);
	}
	else
	{
		len = read(fd, buf, 1024);		
		buf[1023] = '\0';
		printf("APP read : %s\n", buf);
	}
	
	close(fd);
	
	return 0;
}

4.3 驱动程序的Makefile文件

一个驱动程序最简单的Makefile包含以下内容:

# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH,          比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH,          比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin 
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,根据具体情况指定


# KERN_DIR 是指定内核源码的路径的,需要根据不同开发环境指定
KERN_DIR = /home/book/100ask_roc-rk3399-pc/linux-4.4	

all:
	make -C $(KERN_DIR) M=`pwd` modules 
	
	# 本条指令是用于编译应用程序的,放在这里是为了不用在单独编译应用程序而已
	$(CROSS_COMPILE)gcc -o hello_drv_test hello_drv_test.c 

clean:
	make -C $(KERN_DIR) M=`pwd` modules clean
	rm -rf modules.order
	rm -f hello_drv_test

obj-m	+= hello_drv.o		# obj-m 指定具体要编译的驱动程序

5. 在开发板上测试运行hello驱动

编写完上述的代码之后,就可以进行编译了。

直接在hello驱动所在的目录下,输入 make 即可编译了。编译完之后就会看到生成对应的 .ko 文件了。

然后我们把编译好的 .ko 文件,和测试驱动的应用程序都拷到 nfs 共享目录下,我的 nfs 共享目录是在 /home/lbh/nfs 下。

然后我们打开开发板后,进入到开发板的控制台。挂载 ubuntu 中的 nfs 共享目录到开发板的 /mnt 目录下,在开发板输入如下命令:

mount -t nfs -o nolock,nfsvers=3 192.168.1.33:/home/lbh/nfs /mnt

其中 192.168.1.33 这个IP地址是你的ubuntu的IP地址。

挂载完成之后,就可以去 /mnt 目录下看到了自己编译好的 .ko 文件和对应的应用程序文件了。

我们执行 insmod hello_drv.ko 命令,就可以把该驱动程序安装到内核中了。而且可以看到内核打印出了相应的信息,如下:

[ 293.594910] hello_drv: loading out-of-tree module taints kernel.
[ 293.616051] /home/lbh/linux/drv/hello_drv.c hello_init line 70

说明驱动加载成功了。

注意:如果板子没有看到打印信息的话,那么就输入如下命令把内核的打印信息打开:

echo "7 4 1 7" > /proc/sys/kernel/printk

当然有些板子内核打印信息是默认已经打开了的。或者我们输入 demsg 命令也可以看到内核的打印信息。

然后我们运行 hello_drv_test 应用程序来测试内核,都可以看到内核的打印信息,和我们读取到应用程序写给内核的字符串。

6. 和驱动调试有关的其他知识

  1. cat /proc/devices 命令可以查看当前系统是否有我们刚刚安装的驱动程序
  2. 安装了设备驱动之后,就会在我们的Linux系统 /dev目录下生成对应的设备文件了。linux中没一个驱动程序都有一个与之对应的设备文件,可以使用 ls /dev/hello -l 命令查看该驱动文件
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux设备驱动开发入门之——hello驱动 的相关文章

  • 当模式在范围内时使用 sed 打印范围?

    我有一个充满查询的日志文件 我只想查看有错误的查询 日志条目类似于 path to file executing query QUERY SIZE ROWS MSG DURATION 我想打印所有这些东西 但只有当MSG 包含一些有趣的内容
  • 在 UNIX 时间戳 Shell/Bash 中将日期与时区转换

    我需要将日期从格式为 yyyy mm dd hh mm ss TZ 的字符串转换为 UNIX 时间 TZ 时区 到目前为止我所做的是将没有时区的 yyyy mm dd hh mm ss 格式的日期转换为时间戳 dateYMD 2019 2
  • 从 bash 脚本返回值

    我想创建一个返回值的 Bash 文件 意思是 在脚本 script a bash 中我有一定的计算 脚本 script b bash 会调用它 script a bash return 1 5 script b bash a value s
  • 使用 Shell 脚本提供密码

    我已将客户端和服务器设置为无密码登录 就像无密码登录一样 通过将服务器的 RSA 密钥复制到所有客户端的 root ssh id rsa pub 来实现 但这是我手动完成的 我喜欢使用 shell 脚本自动执行此过程 并通过脚本向计算机提供
  • 远程 ssh 命令:第一个回显输出丢失

    我试图通过 ssh 1 liner 调用在远程机器上运行多个命令 方法是将它们指定为传递给 bash c 的分号分隔字符串 它适用于某些情况 但不适用于其他情况 看一下这个 Note the echo 1 output is lost ba
  • 数百个空闲线程的影响

    我正在考虑使用可能数百个线程来实现通过网络管理设备的任务 这是一个在带有 Linux 内核的 powerpc 处理器上运行的 C 应用程序 在每个任务进行同步以将数据从设备复制到任务的初始阶段之后 任务变得空闲 并且仅在收到警报或需要更改一
  • 如何重命名共享库以避免同名冲突?

    我找到了一个图书馆 libjson http sourceforge net projects libjson 我正在尝试将其构建为共享库并在项目中使用 建造很简单 修复 Makefile 错误后 SHARED 1 make install
  • 在命令行上解密使用 PHP openssl_encrypt 制作的文件

    我有一个要加密的字符串 encryptThis Super Secret Text echo openssl encrypt encryptThis aes 128 cbc 1234 FALSE F68A9A229A516752 然后我通过
  • C中的内存使用问题

    请帮忙 操作系统 Linux 其中 sleep 1000 中 此时 top 显示Linux任务 给我写了7 7 MEM使用 valgrind 未发现内存泄漏 我明白 写得正确 所有 malloc 结果都是 NULL 但是为什么这次 睡眠 我
  • 将一个文件写入.c中的另一个文件

    我有一个读取文件然后将其内容复制到另一个文件的代码 我需要使其仅复制每 20 个符号 然后跳过 10 个符号 然后再次跳过 20 个符号 依此类推 我必须使用 lseek 函数 但我不知道如何将所有这些放入循环中来执行此操作 main ar
  • 使用 WGET 运行 cronjob PHP

    我尝试执行一个 cron 并每 5 分钟运行一个 url 我尝试使用 WGET 但我不想下载服务器上的文件 我只想运行它 这是我使用的 crontab 5 wget http www example com cronit php 除了 wg
  • 将用户添加到组但运行“id”时未反映

    R 创建了一个名为 Staff 的组 我希望能够在不以 sudo 身份启动 R 的情况下更新软件包 所以我使用以下方法将自己添加到员工中 sudo usermod G adm dialout cdrom plugdev lpadmin ad
  • :: 右侧的非法标记

    我有以下模板声明 template
  • 让“git pull”在拉取不同分支时要求确认

    当同时处理许多项目和分支时 我偶尔会犯一些愚蠢的错误 比如拉入错误的分支 例如在分支上master I did git pull origin dangerous code并且有一段时间没有注意到这一点 这个小错误造成了很大的混乱 当我尝试
  • 如何随时暂停 pthread?

    最近我开始将 ucos ii 移植到 Ubuntu PC 上 我们知道 在pthread的回调函数中的 while 循环中简单地添加一个标志来执行暂停和恢复是不可能模拟ucos ii中的 进程 的 如下解决方案 因为ucos ii中的 进程
  • 从 gitlab docker runner 启动声纳扫描仪

    我有一个 CI 工作流程 集成了 linting 作业和代码质量作业 我的 Linting 工作是一个 docker runner 从应用程序代码启动我的 eslint 脚本 然后我的代码质量工作应该启动声纳扫描仪泊坞窗实例 检查我的代码并
  • 代码::块 - 警告:GDB:无法设置控制终端:不允许操作

    我已经通过官方存储库在 Ubuntu 14 04 中安装了 Code Blocks 13 12 当我编译时 一切正常 但是当我调试时 shell 中会显示以下消息 警告 GDB 无法设置控制终端 操作不正确 允许的 程序执行到断点 但当我执
  • sed 仅最后一个匹配模式

    我想sed仅文本文件的最后一个匹配模式 输入文件 boy boy girl boy 输出文件 boy boy girl boys 一种方法是反转文件 仅替换第一个匹配项 然后再次反转 tac
  • bash双括号问题

    我对 bash 脚本非常陌生 在使用双括号时遇到了问题 我似乎无法让它们在 Ubuntu Server 11 10 中工作 我的下面的脚本位于 if test sh 中 bin bash if 14 14 then echo FOO fi
  • 在键盘热插拔上加载模块

    我正在尝试学习如何为 Linux 系统编写模块和驱动程序 类似于this https unix stackexchange com questions 120839 usb kernel module does not load on de

随机推荐

  • 【华为OD机试】乱序整数序列两数之和绝对值最小【2023 B卷

    华为OD机试 真题 点这里 华为OD机试 真题考点分类 点这里 题目描述 给定一个随机的整数 可能存在正整数和负整数 数组 nums 请你在该数组中找出两个数 其和的绝对值 nums x nums y 为最小值 并返回这个两个数 按从小到大
  • 安卓开发--应用市场的界面制作(一)--viewpager+fragment实现可滑动的底部导航栏

    相关文章 前言 布局文件 MainAcivity 细节 相关文章 Android Fragment完全解析 关于碎片你所需知道的一切 这是一片很不错的博文 基本上看完就懂fragment是个什么情况了 ViewPager防止Fragment
  • 什么是智慧矿山解决方案发展方向?企业又该怎么做呢?

    什么是智慧矿山呢 智慧矿山是指采用现代高新技术和全套矿山自动化设备等等一些新兴的技术来提高矿山的生产效率和经济效益 通过对矿山生产过程进行实时动态监控 通过这些措施能够让矿山生产维持在最佳状态和最优水平 新型数字化技术能够帮助传统矿业在生产
  • STM32 PWM输出

    STM32 PWM输出 工作过程 我们假定定时器工作在向上计数PWM 模式 且当 CNT
  • 天梯赛座位分配PTA

    天梯赛座位分配PTA 天梯赛座位分配PTA TOC 前言 PTA的一道题目 一 题目内容 题目 天梯赛每年有大量参赛队员 要保证同一所学校的所有队员都不能相邻 分配座位就成为一件比较麻烦的事情 为此我们制定如下策略 假设某赛场有 N 所学校
  • QT学习_02_Lambda表达式——槽函数的正确打开方式

    1 带参数的信号 假设发送的信号带参数 信号是可以重载的 我用同一个函数名称 一个带参数 一个不带参数 在点击 切换到主窗口 按钮的时候 同时发射这两个信号 subwidget h ifndef SUBWIDGET H define SUB
  • ubuntu学习笔记

    ubuntu学习笔记 前言 一 ubuntu 22 04 更换阿里源 二 ubuntu22 04安装搜狗输入法 三 linux系统分区 个人向 四 vi及vim文本编辑器使用 五 nano文本编辑器使用 六 查看IP及网关DNS并设置IP地
  • 第09课:生活中的工厂模式——你要拿铁还是摩卡

    用程序来模拟生活 从剧情中思考工厂模式 工厂模式的模型抽象 类图 模型说明 简单工厂的优点 简单工厂的缺点 模型的拓展应用 应用场景 拓展 工厂三姐妹 简单工厂模式 工厂方法模式 抽象工厂模式 进一步思考
  • 遇到问题--Nginx---tomcat启动web程序访问静态资源时404找不到

    http blog csdn net zzq900503 article details 76927074 给web站点配置域名转发后 tomcat启动web程序访问静态资源时404找不到 经过确认项目资源路径都没问题 后来经过排查后发现是
  • 配置samba服务

    什么是samba服务 是 和windows 进行 文件打印机共享的组件 结果就是linux windows 之间可以互相访问它们的共享文件 说明 我用的是ubuntu系统 ubuntu系统安装samba服务 确定自己是否安装samba dp
  • (一)Google发布了一个新的Tensorflow物体识别API

    做图像识别有很多不同的途径 谷歌最近发布了一个使用Tensorflow的物体识别API 让计算机视觉在各方面都更进了一步 这篇文章将带你测试这个新的API 并且把它应用在youtube上 可以在GitHub上获取用到的全部代码 https
  • JDBC连接数据库URL示例

    jdbc mysql localhost 3306 crm useSSL false useUnicode true characterEncoding UTF 8
  • matlab扩充内存,matlab中内存不够用的解决方案

    1 在命令行中输入pack函数来整理内存 pack函数到底是什么机制呢 这里参考了MATLAB的help文档 话说回来 help始终是学习MATLAB的 金参考标准 用法 pack pack filename pack filename 第
  • Struts配置文件详解

    Struts1配置文件
  • 最好的工程师像投资者一样思考,而不是建设者

    我在大学期间住在图书馆 我学习的教科书理论越多 我就会成为一名更好的工程师 我想 然而 当我开始工作时 我注意到业内最优秀的工程师并不一定比应届毕业生了解更多的理论 他们只是带来了不同的心态 即投资者的心态 正是这种心态帮助他们提出更聪明的
  • 谈一谈,自身对技术经理这个职位的理解吧

    前言 19年初在上一家公司离职 在上一公司服役了4年半 成长了不少 收获了不少东西 在上一公司也带过很多团队 多的时候6 7个人 少的时候2人 也总结了很多的所谓的经验吧 由于一系列原因吧 离职来到了我现在的公司 岗位职责 到这边以后 入职
  • 什么是MyBatis

    一 MyBatis概述 1 1 原始的JDBC操作 谈及mybatis 必然需要先了解Java和数据库的连接技术 JDBC Java DataBase Connectivity 但是原始JDBC操作中 却存在如下缺点 数据库连接创建 释放频
  • 计算机制作节日贺卡教案,《制作节日贺卡》教学设计.doc

    文档介绍 制作节日贺卡 教学设计 小学信息技术 第五册第1单元第3课吴信平肥西县三河镇三河学区中心学校电子邮箱 ahicon 电话 教材分析本课是制作电子贺卡单元的第3课 教材内容为指导学生完整地制作一张贺卡 让学生学会如何制作一个简单的电
  • Spark是否能替代Hive

    在实际生产环境中已经形成了离线以Hive为主 Spark为辅 实时处理用Flink的大数据架构体系及Impala Es Kylin等应用查询引擎 但是有很多学习Spark的程序员普遍认为Spark必然会替代Hive成为新的一代大数据仓库标准
  • Linux设备驱动开发入门之——hello驱动

    1 Linux驱动程序的分类 Linux 中主要分为三大类驱动 字符设备驱动 块设备驱动和网络设备驱动 1 字符设备驱动 因为软件操作设备是是以字节为单位进行的 是按照字节流进行读写操作的一种设备 典型的如LCD 蜂鸣器 SPI 触摸屏等驱