Linux操作系统
1,Linux操作系统基础:
1),基础知识
第一块处理器:intel 4004
Intel 8008 ,i8086,…协处理器(主从机制); 80386,80586,奔腾,酷睿,四核;
1971年IBM发布个人电脑5150;
系统级:系统软件
用户级: 应用程序
计算机5大部分组成:运算器,控制器,存储器,输入设备,输出设备。
1,指令与数据以同等地位存于存储器,按地址寻访
2,指令和数据用二进制表示(计算器只认识二进制)
3,指令由操作码和地址码组成
4,存储程序(RAM或ROM)
5,以运算器为中心
蓝色的线:地址总线(寻址)
绿色的线:数据总线(传数据)
红色的线:控制总线(控制指令)
方法一:采用单位时间内平均执行指令条数来衡量。
单位:MIPS(每秒执行多少百万条指令)
方法二:主频及时钟频率,指计算机的CPU在单位时间内发出的脉冲数
单位:MHz(兆赫兹)目前芯片技术不能无限提高主频,因为有功耗大等负面作用。
存储器:
半导体存储:TTL(易丢失)
磁表面存储器,磁头,载磁体(不易丢失)
磁芯存储器,硬磁材料,环状原件(不易丢失)
光盘存储器:激光,磁光材料(不易丢失)
Linux操作系统:
ubuntu是安卓官方支持的,支持arm架构。
特点:
硬件要求比较低。
文件系统
存储和组织计算机文件和资料的方法。
通用结构:
1,引导块
2,超级块
3,iNode
分类
磁盘文件系统(NTFS,EXT3)
闪存文件系统(JFFS2,YAFFS)
数据库文件系统(BFS,WINFS)
网络文件系统(NFS)
虚拟文件系统VFS(PROC)
步骤
0,检测系统信息
1,拷贝安装文件
2,安装系统 install system
3,检索文件
4,配置硬件
5,配置boot
一切皆是文件
一般文件:-
目录文件:d
链接文件:l
块设备:b
字符设备:c
socket:s
管道:p
文件属性:
rwx read write x运行
Linux目录结构:
/bin:存放系统可执行文件
/sbin:存放管理员用的可执行文件
/etc:配置文件
/lib:共享类库
/dev:设备
/tmp:临时文件
/boot:启动文件
/root:root用户目录
/mnt:挂载目录
/opt:优化目录-优化程序
/usr:用户程序目录(类似programmer Files)
/usr/bin(sbin):用户的可执行文件目录
/var:系统变量
/proc:虚拟文件系统 VFS
/lost&found:找回文件
/home:类似于users文件夹,用户文档,根据用户来创建文件夹
根据颜色划分:
蓝色:目录
绿色:可执行文件
红色:压缩文件
浅蓝色:链接文件
灰色:其他文件
ls的实现机制:
dir的实现机制:输入命令,系统在系统目录下找应用程序,依托命令程序,win32api调用底层。
ls:输入ls,到相应的目录找shell程序(可执行文件),从path里找。找到之后调用。基于shell的基本库。采用的是bashShell,zShell,cShell等。
GNU:GNU`s Not Unix 的递归缩写。
CNU计划:又称为革权计划,是由Richard Stallman在1983年9月27日公开发起的。他的目标是创建一套完全只有的操作系统。
1985年创立了FSF。
操练Shell命令:
1,命令格式,命令行编辑,Shell特殊字符,练级帮助命令,命令行历史记录,命令行别名。
command [-options] parameter1 parameter2......
指令 选项 参数1 参数2
linux中区分大小写。
管道(pipe):作用是把前面进程的输出作为后面进程的输入!把前面文件的输出作为后面文件的输入。
输入输出的重定向:>show.txt
命令行别名:alias 别名 “命令”
sudo:超级用户权限执行命令
GNU开发环境:
研究GCC和GDB的使用,掌握Eclipse Linux下的使用。
通过研究GCC源代码,分析GCC程序的关键结构,进而将来扩展编译器的编写。
GCC:(GNU C Compiler)是GNU项目中符合ANSI C标准的编译系统,能够编译用C,C++,Object C等语言编写的程序,同时gcc也是一个交叉编译器,特别适用于不同平台的嵌入式开发。
GCC支持的常用的后缀名:
.c c原始文件
.C/.cc/.cxx c++原始文件
.o 目标文件
.a 编译后的库文件。
.so 静态和动态库
编译静态链接库
先生成目标文件.o
ar crv[*.a][*.o]
调用静态链接库
gcc -o[file][file.c] -L
编译动态链接库
1,生成位置无关的目标代码 gcc -fPIC -c[*.c]
2,gcc -shared -o[*.so][*.o]
调用动态链接库
gcc -o [file] [file.c]-L [file.so]
隐式调用:和静态链接库调用没区别
显示调用:编译源码的时候调用,加内容在代码里
编译器
源程序》词法分析器》语法分析器》语义分析器》中间代码生成器》代码优化器》代码优化器》代码生成器》目标程序
gdb调试:
分析第一个调试器的行为:
debug模式编译
打断点
运行调试
打印和监控值
makefile:
作用:工程文件组织,编译成复杂的程序。安装及卸载我们的程序。
makefile的编写规则:
hello:main.o func1.o func2.o
gcc main.o func1.p func2.o -o hello
规则
targets(目标) : prerequisites(依赖)
command(命令)
hello是目标
后面三个文件是依赖
调用后面一行的命令完成这个功能。
makefile中的变量
用户自定义变量
autotools
一个用来自动化生成makefile的工具软件。linux系统下贼好用。
Linux上层开发与Kernel的关系:
内核去:linux有自己的进程管理方式,
打开文件:一个应用程序通过要求内核打开相应的文件,宣告他要访问一个IO设备,内核返回一个非负证书,叫描述符号(Descriptor)。
读文件:从文件拷贝n>0个字节到存储器。写文件:从存储器拷贝n>0个字节到文件。
GNU Linux IO操作类别:
文件及流的标准输入输出
底层输入输出
文件系统接口
管道及FIFO(先入先出队列)
Socket
底层终端接口
主要数据结构介绍
FD:文件描述符,对于内核而言,所有打开的文件都由文件描述符引用。文件描述符是一个非负整数。打开一个现存文件或创建一个新文件时,向内核进程返回一个描述符。当读,写一个文件时,用open或create返回的文件描述符fd标识该文件,将其作为参数传送给read或write。在POSIX。1应用程序中,文件描述符为常数0,1和2分别岱庙STDIN_FIENO,STDOUT_FILENO和STDERR_FILENO,意即标准输入,标准输出和标准出错输出。这些常数都定义在头文件<unistd.h>中。文件表舒服的范围是0-OPEN_MAX,在目前常用的linux系统中,是32位整形所能表示的整数,即65535,64位机上则更多。
File:
Files Structure:
如何查资料:
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic main restricted universe multiverse
deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-updates main restricted universe multiverse
deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-updates main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-backports main restricted universe multiverse
deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-backports main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-security main restricted universe multiverse
deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-security main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-proposed main restricted universe multiverse
deb-src https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ bionic-proposed main restricted universe multiverse
C
C语言最早是在1972年在DEC PDP-11计算机上被首次实现。
在1978年,布莱恩·柯林汉和丹尼斯·里奇制作了C的第一个公开可用描述。
特点:
易于学习
结构化语言
产生高效率的程序
处理底层的活动
可以在多种计算机平台上编译
C11:
也被称为C1X
对齐处理(Alignment)的标准化(包括Alignas标志位,alignof运算符,aligned_alloc函数以及<stdalign.h>头文件)。
Noreturn函数标记,类似于gcc的__attribute_((noreturn))
__Generic关键字
多线程(Multihreading)支持,包括
_Thread_local存储类型标识符,<thread.h>头文件,里面包含了线程的创建和管理函数
_Atomic类型修饰符和<stdatomic.h>头文件
增强的Unicode的支持,基于Cunicode技术报告。增加了char16_t和char32_t数据类型,提供了包含unicode字符串转换函数的头文件<uchar.h>
删除了gets()函数,使用一个新的安全的函数,gets_s()替代。
增加了边界检查函数接口,定义了新的安全的函数,例如fopen_s(),start_S()等等。
增加了更多浮点处理宏。
匿名结构体/联合体支持。这个在gcc早已存在,C11将其引入标准。
静态断言(Static assertions),_Static_assert(),在解释#if和#error之后被处理。
新的fopen()模式。类似POSIX中的 O_CREAT|O-EXCEL,在文件锁中比较常用。
新增quick_exit()函数作为第三种终止程序的方式。党exit()失败时可以做最少的清理工作。文章来源:https://www.toymoban.com/news/detail-412536.html
第一个字符设备驱动开发笔记
字符设备驱动框架
字符设备驱动的编写主要就是驱动对应的open,close,read。其实就是file_operations结构体的成员变量的实现。
驱动模块的加载与卸载
Linux驱动程序可以编译到kernel里面,也就是zImage,也可以编译为模块,.ko。测试的时候只需要加载.ko模块即可。
编写驱动的时候注意事项:
1,编译驱动的时候需要用到linux内核源码,因此要解压缩linux内核源码。编译linux内核源码!因此要解压缩linux内核源码,编译linux内核与那么。得到zImage和.dtb。需要使用编译后得到的zImage和deb启动系统。
字符设备的注册与注销
1,我们需要向系统注册一个字符设备,使用函数register_chrdev。
2,卸载驱动的时候需要注销掉前面注册的字符设备,使用函数unregister_chrdev,注销字符设备
设备号
1,Linux中内核使用dev_t
2,linux内核将设备号分为两个部分:主设备号和次设备号。主设备号占用前12位,次设备号占用低20位。主设备号的最大值位4095,及2的12次方。
3,设备号的擦欧总函数,或宏。
从dev_t获取主设备号和次设备号,MAJOR(dev_t);,MINOR(dev_t)。也可以使用主设备号和次设备号构成dev_t,通过MKDEV(major minor)。
file_operations的具体实现
struct file_operations {
struct module *owner;
loff_t (*llseek) (struct file *, loff_t, int);
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
int (*iterate) (struct file *, struct dir_context *);//
int (*iterate_shared) (struct file *, struct dir_context *);//
unsigned int (*poll) (struct file *, struct poll_table_struct *);
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
int (*flush) (struct file *, fl_owner_t id);
int (*release) (struct inode *, struct file *);
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
int (*fasync) (int, struct file *, int);
int (*lock) (struct file *, int, struct file_lock *);
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
int (*check_flags)(int);
int (*flock) (struct file *, int, struct file_lock *);
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
int (*setlease)(struct file *, long, struct file_lock **, void **);
long (*fallocate)(struct file *file, int mode, loff_t offset,
loff_t len);
void (*show_fdinfo)(struct seq_file *m, struct file *f);
#ifndef CONFIG_MMU
unsigned (*mmap_capabilities)(struct file *);
#endif
ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,
loff_t, size_t, unsigned int);
int (*clone_file_range)(struct file *, loff_t, struct file *, loff_t,
u64);
ssize_t (*dedupe_file_range)(struct file *, u64, u64, struct file *,
u64);
};文章来源地址https://www.toymoban.com/news/detail-412536.html
这里面有很多地方都用到了__user字符。
它其实是一种形式的文档而已,表明指针是一个用户空间地址,因此不能被直接引用。对通常的编译来将,__user没有任何效果,但是可由外部检查软件使用,用来寻找对用户空间的错误使用。
struct module *owner;
第一个file_operations字段并不是一个操作,相反,它是一个指向拥有该结构的模块的指针。内核使用这个字段以避免在模块的操作正在被使用时卸载该模块。几乎在所有情况下,该成员都会被初始化为THIS_MODULE,它是定义在<linux/module.h>中的一个宏。
loff_t (*llseek) (struct file *, loff_t, int);
llseek 方法用作改变文件中的当前读/写位置, 并且新位置作为(正的)返回值。
(指针参数struct file *为进行读取信息的目标文件结构体指针;参数 loff_t 为文件定位的目标偏移量;参数int为对文件定位的起始地址,这个值可以为文件开头(SEEK_SET,0,当前位置(SEEK_CUR,1),文件末尾(SEEK_END,2))
loff_t 参数是一个"long offset", 并且就算在 32位平台上也至少 64 位宽. 错误由一个负返回值指示;如果这个函数指针是 NULL, seek 调用会以潜在地无法预知的方式修改 file结构中的位置计数器。
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
用来从设备中读取数据。该函数指针被赋值为NULL值时,将导致read系统调用出错并返回-EINVAL("Invalid argument,非法参数")。函数返回非负值表示成功读取的字节数(返回值为"signed size"数据类型,通常就是目标平台上的固有整数类型)。
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
向设备发送数据。如果没有这个函数,write系统调用会向程序返回一个-EINVAL。如果返回值非负,则表示成功写入的字节数。
ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);
异步的读取操作,在函数返回之前可能不会完成的读取操作。如果该函数为NULL,所有的操作将通过read(同步)处理。
linux 4.5版本前为:
ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);
异步写入操作。
linux 4.5版本前为:
ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
unsigned int (*poll) (struct file *, struct poll_table_struct *);
poll方法是poll、epoll和select这三个系统调用的后端实现。这三个系统调用可用来查询某个或多个文件描述符上的读取或写入是否被阻塞。poll方法应该返回一个位掩码,用来指出非阻塞的读取或写入是否可能,并且也会向内核提供将调用进程置于休眠状态直到I/O变为可能时的信息。如果驱动程序将poll方法定义为NULL,则设备会被认为即可读也可写,并且不会被阻塞。
long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
ioctl提供了一种执行设备特定命令的方法(如格式化软盘的某个磁道,这即不是读操作也不是写操作)。另外,内核还能识别一部分ioctl命令,而不必调用fops表中的ioctl。如果设备不提供ioctl入口点。这对于任何内核未预先定义的请求,ioctl系统调用将返回错误(-ENOTTY,“No such ioctl for device,该设备无此ioctl命令”)。
1、compat_ioctl:支持64bit的driver必须要实现的ioctl,当有32bit的userspace application call 64bit kernel的IOCTL的时候,这个callback会被调用到。如果没有实现compat_ioctl,那么32位的用户程序在64位的kernel上执行ioctl时会返回错误:Not a typewriter
2、如果是64位的用户程序运行在64位的kernel上,调用的是unlocked_ioctl,如果是32位的APP运行在32位的kernel上,调用的也是unlocked_ioctl
int (*mmap) (struct file *, struct vm_area_struct *);
mmap用于请求将设备内存映射到进程地址空间。如果设备没有实现这个方法,那么mmap系统调用将返回-ENODEV。
int (*open) (struct inode *, struct file *);
尽管这始终是对设备文件执行的第一个操作,然而却并不要求驱动程序一定要声明一个相应的方法。如果这个入口为NULL,设备的打开操作永远成功,但系统不会通知驱动程序。
int (*flush) (struct file *, fl_owner_t id);
对flush操作的调用发生在进程关闭设备文件描述符的时候,它应该执行(并等待)设备上尚未完结的操作。请不要将它同用户程序使用的fsync操作相混淆。目前,flush仅仅用于少数几个驱动程序,比如SCSI磁带驱动程序用它来确保设备被关闭之前所有的数据都被写入磁带中。如果flush被置为NULL,内核将简单地忽略用户应用程序的请求。
int (*release) (struct inode *, struct file *);
当file结构被释放时,将调用这个操作。与open相仿,也可以将release置为NULL。
注意:release并不是在进程每次调用close时都会被调用。只要file结构被共享(如在fork或dup调用之后),release就会等到所有的副本都关闭之后才会得到调动。如果需要在关闭任意一个副本时刷新那些待处理的数据,则实现flush方法。
int (*fsync) (struct file *, loff_t, loff_t, int datasync);
该方法是fsync系统调用的后端实现,用户调用它来刷新待处理的数据。如果驱动程序没有实现这一方法,fsync系统调用返回-EINVAL。
int (*fasync) (int, struct file *, int);
这个操作用来通知设备起FASYNC标志位发生了变化。如果设备不支持异步通知,该字段可以是NULL。
int (*lock) (struct file *, int, struct file_lock *);
lock方法用于实现文件锁定,锁定是常规文件不可缺少的特性,但设备驱动程序几乎从来不会实现这个方法。
ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
由内核调用,将数据发送到对应的文件,每次一个数据页。设备驱动程序通常也不需要实现sendpage。
unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
该方法的目的是在进程的地址空间中找到一个合适的位置,以便将底层驱动设备中的内存段映射到该位置。该任务通常由内存管理代码完成,但该方法的存在可允许驱动程序强制满足特定设备需要的任何对齐需求。大部分驱动程序可设置该方法为NULL。
int (*check_flags)(int);
该方法允许模块检查传递给fcntl(F_SETFL...)调用的标志。
字符设备驱动你框架的搭建
应用程序编写
linux下一切皆文件,操作一个文件就得先打开文件。
chrdevbase虚拟设备驱动对的编写
测试
1,加载驱动。
mpdprobe chrdevbase.ko
2,进入dev查看设备文件,以模块名字命名的 chrdevbase. /dev/chrdevbase.但是实际上没有,我们没有全新常见。
chrdevbase虚拟设备驱动的完善
要求:应用程序可以对驱动读写操作,读的话就是从驱动锅里面读取字符串,写的话就是应用程序想驱动写字符串。
1,chrdevbase_read驱动函数编写:
1),驱动给应用传递数据的时候需要用到copy_to_user(void __user *to,const void *from,unsigned long n)函数。参数to表示目的,参数from表示源,参数n表示要复制的数据长度。如果复制成功,返回值为0,如果复制失败则返回负数。
2),应用程序向驱动程序传递数据的时候要用到copy_from_user():讲buf中的数据复制到写缓冲区writebuf中, 因为用户空间内存不能直接访问内核空间的内存,所以需要借助函数copy_from_user将用户空间的数据复制到writebuf这个内核空间中。
名词解释:
1,major:主设备号,Linux下每个设备都有一个设备号,设备号分为主设备号和次设备号两部分。
2,name:设备名字,指向一串字符串。
3,fops:结构体fileoperations类型指针,只想设备的操作函数集合变量。
到了这里,关于linux驱动开发笔记一的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!