高级特性

  • 代码精炼
  • python代码量越少,开发效率越高.

切片

  • 取list或tuple的部分元素

  • 示例

    1
    2
    L[0:3] #0到3,但结果不包括索引3
    L[:3] #0可省略
    1
    2
    L[-2:]  #-2到0 但不包括索引0
    L[-2:-1] #-2
    1
    2
    3
    L[:10:2] #前10 每隔2个取一个
    L[::5] #所有,每隔5个取一个
    L[:] #原样复制
  • 对tuple取切片,结果依然是tuple.

  • 字符串可以看作一个list,每个字符占一位.

迭代

  • 迭代很随便…太随便了…

  • list tuple不多说了

  • dict字典也可以迭代,因为无序,输出的顺序不一定相同.
    默认迭代的是key 但value 也可以迭代,key value也可以同时迭代

    1
    2
    for value in d.values()
    for k, v in d.items()
  • 上文书中说的,字符串可以当作list,所以也可以迭代.

  • 问题来了,如何判断可迭代对象?
    collections模块的Iterable类型判断

    1
    2
    3
    >>> from collections import Iterable
    >>> isinstance('abc', Iterable) # str是否可迭代
    True
  • 类似c/java的带下标循环实现?
    Python内置的enumerate函数,把一个list变成索引-元素对

    1
    2
    for i, value in enumerate(['A', 'B', 'C']):
    print(i, value)

列表生成式

  • 用来生成list

  • 示例: [1x1, 2x2, 3x3, …, 10x10]

    1
    2
    [x * x for x in range(1, 11)]
    [m + n for m in 'ABC' for n in 'XYZ'] #两层
  • for可以同时循环两个甚至多个变量,dict的items()可以同时迭代key和value

    1
    2
    3
    d = {'x': 'A', 'y': 'B', 'z': 'C' } # 多个变量
    [k + '=' + v for k, v in d.items()] # 生成list
    [s.lower() for s in L] # 全部小写

生成器

  • generator 依照某种算法不断循环生成数据,而不是一次性生成完.节省大量空间.

  • 创建generator

    • 把列表生成式的[]改成()

      1
      g = (x * x for x in range(10))

      调用next(g)可获取下一个值.
      最常用for n in g:代入for循环.也没有抛出错误.

    • 定义一个包含yield关键字的函数.

      1
      2
      3
      4
      5
      6
      7
      def fib(max):
      n, a, b = 0, 0, 1
      while n < max:
      yield b
      a, b = b, a + b
      n = n + 1
      return 'done'
      • 变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
      • 用for循环调用generator时,拿不到generator的return语句的返回值。如果想要拿到返回值,必须捕获StopIteration错误,返回值包含在StopIteration的value中.

迭代器

  • 可以被for循环的对象统称为可迭代对象:Iterable
    使用isinstance()判断一个对象是否是Iterable对象

    1
    isinstance({}, Iterable)
  • 可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator 表示一个惰性计算的序列 Iterator甚至可以表示一个无限大的数据流
    isinstance()判断一个对象是否是Iterator对象

    1
    isinstance((x for x in range(10)), Iterator)
  • 集合数据类型如list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象。

  • Python的for循环本质上就是通过不断调用next()

函数

常用

  • 参数数量不对 TypeError错误

  • 参数类型错误,TypeError的错误,并且给出错误信息

  • int()转换为int类型

  • float()

  • bool()

  • str()

  • 函数名为一指向函数对象的引用,可以将变量指向函数,再调用

    1
    2
    3
    >>> a = abs # 变量a指向abs函数
    >>> a(-1) # 所以也可以通过a调用abs函数
    1

定义函数

  • 示例

    1
    2
    3
    4
    5
    def my_abs(x):
    if x >= 0:
    return x
    else:
    return -x

    def 函数名(参数) :
    函数体
    return

  • 没有return 则返回 None

  • import 包含模块,java一样

  • 空函数 pass 语句.

    1
    2
    def nop():
    pass
  • 示例 返回多个参数

    1
    2
    3
    4
    5
    6
    import math

    def move(x, y, step, angle=0):
    nx = x + step * math.cos(angle)
    ny = y - step * math.sin(angle)
    return nx, ny

    返回的是一个tuple .. 按位置赋值给变量

函数参数

  • 参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数

默认参数

  • 示例:

    1
    2
    3
    4
    def power(x, n=2):
    ...

    power(5) power(5, 2) power(n=3,5)
  • 默认参数必须指向不变对象

  • 多个参数时,变化大的参数在前,变化小的参数在后。变化小的参数就可以作为默认参数。

  • 多个默认参数,可以按顺序提供默认参数

  • 也可以不按顺序提供。当不按顺序提供部分默认参数时,需要把参数名写上 enroll('Adam', 'M', city='Tianjin')

    • 默认参数 不可变性 每次调用均会改变其值.

可变参数

  • 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    def calc(*numbers):
    sum = 0
    for n in numbers:
    sum = sum + n * n
    return sum

    nums = [1, 2, 3]
    calc(*nums) #相同
    calc(1, 2, 3)
  • 传入的参数个数可变,传入组装成了tuple

  • 当作c语言指针吧😂 传入数组的地址

命名关键字参数

  • 示例:

    1
    2
    3
    4
    def person(name, age, *, city, job):
    print(name, age, city, job)

    person('Jack', 24, city='Beijing', job='Engineer')
  • 命名关键字参数需要一个特殊分隔符后面的参数被视为命名关键字参数

  • 函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*

    1
    2
    def person(name, age, *args, city, job):
    print(name, age, args, city, job)
  • 如果没有可变参数,就必须加一个作为特殊分隔符。如果缺少,Python解释器将无法识别位置参数和命名关键字参数

关键字参数

  • 示例

    1
    2
    3
    4
    5
    6
    7
    8
    def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)

    extra = {'city': 'Beijing', 'job': 'Engineer'}
    person('Jack', 24, city=extra['city'], job=extra['job'])
    #相同
    extra = {'city': 'Beijing', 'job': 'Engineer'}
    person('Jack', 24, **extra)
  • 关键字参数在函数内部组装为一个dict

  • 函数内部获得是 dict的拷贝,修改对原值无影响.

  • 可以传入任意不受限制的关键字参数

递归函数

  • python 不含尾递归优化,注意层级,否则非常容易堆栈溢出.

linux下文件夹相关操作

  • 参考

    http://blog.csdn.net/u011118014/article/details/43232693

    打开文件夹,遍历访问每个文件

切换到文件夹路径下

  • 调用 chdir 即可

    1
    chdir("/info");

打开文件夹

  • 源码,打开文件夹返回对应文件夹的DIR结构体

    1
    2
    3
    4
    5
    DIR *dir;
    dir = opendir(pcDirName);
    if (NULL == dir){
    continue ;
    }
  • DIR结构体

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    struct __dirstream
    {
    void *__fd;
    char *__data;
    int __entry_data;
    char *__ptr;
    int __entry_ptr;
    size_t __allocation;
    size_t __size;
    __libc_lock_define (, __lock)
    };
    typedef struct __dirstream DIR;

    保存文件夹相关内容,无需深究

  • 源码,遍历文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    struct direct    *ent;
    while (NULL != (ent = readdir(dir))){
    /*如果 指向 . ..*/
    if (0 == strcmp(ent->d_name, ".") \
    || 0 == strcmp(ent->d_name, "..") \
    || 4 == ent->d_type){
    continue;
    }
    /*遍历*/
    }
  • dirent结构体

    1
    2
    3
    4
    5
    6
    7
    8
    struct dirent
    {
    long d_ino; /* inode number 索引节点号 */
    off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
    unsigned short d_reclen; /* length of this d_name 文件名长 */
    unsigned char d_type; /* the type of d_name 文件类型 */
    char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */
    }

    dirent指向目录和目录中某个具体文件,但还是桥梁作用,访问文件具体内容还需要通过d_name找到stat结构体支援.

  • 源码,获取stat结构体

    1
    2
    stat  f_stat;
    sdwRet = stat(ent->d_name, &f_stat);
  • stat结构体是指向文件的结构体

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    struct stat {
    mode_t st_mode; //文件访问权限
    ino_t st_ino; //索引节点号
    dev_t st_dev; //文件使用的设备号
    dev_t st_rdev; //设备文件的设备号
    nlink_t st_nlink; //文件的硬连接数
    uid_t st_uid; //所有者用户识别号
    gid_t st_gid; //组识别号
    off_t st_size; //以字节为单位的文件容量
    time_t st_atime; //最后一次访问该文件的时间
    time_t st_mtime; //最后一次修改该文件的时间
    time_t st_ctime; //最后一次改变该文件状态的时间
    blksize_t st_blksize; //包含该文件的磁盘块的大小
    blkcnt_t st_blocks; //该文件所占的磁盘块
    };
  • 之后愉快访问吧

数据类型

  • 整数 浮点数

字符串

  • 字符串以单引号 ‘ 或 “ 包括.
  • 转义字符 \ 依然有效. r’’表示此字符串默认不转移.
  • /n有效, 可用 开头 … 替换

布尔

  • 只有True 和 Flase
  • and or not 3种常用操作

常量

  • 通常都为大写表示.
  • 除法有两种 / 或 //
    / 结果可能为浮点, // 结果为整数(地板除)

其他

  • None 空值,不为0.谨记
  • 动态语言,so,变量的类型无所谓.
  • 浮点数精度无限,但超过限制直接表示为 inf

字符编码

  • 简而言之 文件使用utf-8就对了.
  • utf-8 属于万国码的简化,低位与 ASCII 兼容.

python字符串编码

  • py3中默认是 Unicode 万国码.

  • ord()获取字符的整数表示,chr()把编码转换为字符.

  • Python对bytes类型的数据用带b前缀的单引号或双引号表示 x = b'ABC'

  • 以Unicode表示的str通过encode()方法可以编码为指定的bytes

    1
    2
    3
    4
    'ABC'.encode('ascii')
    b'ABC'
    '中文'.encode('utf-8')
    b'\xe4\xb8\xad\xe6\x96\x87'
  • 对应的decode()方法

    1
    2
    3
    4
    >>> b'ABC'.decode('ascii')
    'ABC'
    >>> b'\xe4\xb8\xad\xe6\x96\x87'.decode('utf-8')
    '中文'

    可以传入errors=’ignore’忽略错误的字节

    1
    b'\xe4\xb8\xad\xff'.decode('utf-8', errors='ignore')
  • len(),str计算字符.bytes计算字节

  • 开头,第一行,可执行文件(windows会忽视注释)
    第二行,以utf-8处理文件.

    1
    2
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-

字符串格式化

  • 占位符与c基本相同 %d %s %x %f

    1
    2
    print('%2d-%02d' % (3, 1))
    print('%.2f' % 3.1415926)

    需要 % 时, %%

  • format()

    1
    2
    >>> 'Hello, {0}, 成绩提升了 {1:.1f}%'.format('小明', 17.125)
    'Hello, 小明, 成绩提升了 17.1%'

list tuple

list 列表

  • 示例, = [ , , ] 可变 有序

    1
    classmates = ['Michael', 'Bob', 'Tracy']
  • 索引以 0 开始,可以倒序 -n 当然越界都是 IndexError 错误

  • .len(),list元素个数.

  • .append(x) 末尾添加

  • .insert(i,x) 对应索引处插入

  • .pop(i) 删除i对应元素,为空则删除末尾元素.

  • 替换i位置,直接赋值.

  • list的元素可以为一个list.多重数组.

tuple 元组

  • 示例= ( , , ) 不可变 有序 更安全,其他与list相同

    1
    t = (1, 2)
  • t = (1,)声明一个元素元组,比较特殊.

条件判断

  • 示例

    1
    2
    3
    4
    5
    6
    if true :
    print("1")
    elif true :
    print("2")
    else :
    print("3")
  • if 条件可简写,if x x非零数值、非空字符串、非空list 则为true

  • 提及用户输入 input()

  • input默认返回字符串,如需要其他类型 int()转换.

循环

  • 示例 与c相同

    1
    2
    for x in names:
    print(x)
  • 示例 与c相同

    1
    2
    while n > 0:
    print(n)
  • break 跳出此层循环

  • continue 跳出本轮循环

dict 和 set

dict

  • 字典,使用键值对储存数据,索引极快,空间换时间.

  • 对应其他语言的 map

  • 示例: {‘’: ,’’: ,…}

    1
    2
    3
    d = {'Michael': 95, 'Bob': 75, 'Tracy': 85}
    d['Michael']
    95
  • key

    • 必须为不可变对象,整数、字符串等

    • 一个key对应一个value

    • key不存在

      • in判断

        1
        2
        >>> 'Thomas' in d
        False
      • get()获取

        1
        2
        d.get('Thomas') # 返回空
        d.get('Thomas', -1) # 不存在? 返回-1: ;
    • pop()删除

      1
      d.pop('Bob')

set

  • key的集合,但不存在value

  • 无序、不重复、元素为不可变对象.

  • set可以看作是一个无序和无重复元素的集合.

  • 创建set需要一个list

  • 示例 set([ , , …])

    1
    2
    3
    >>> s = set([1, 2, 3])
    >>> s
    {1, 2, 3}
  • .add(key)/.remove(key)

  • 不同的set之间可以交集(&)取并集(|).


编程环境

  • Android Studio 3.0.1

问题

  • MyPrivacy 在android M上闪退,在模拟器中复现.提示
    1
    android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity  context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

过程

  • 进入第二个Activity应用设置时,才出现,而且 7.1无问题.模拟器复现,抓log.

解决

  • log的意思是启动activity的context不是 activity.
  • 对应代码
    1
    2
    3
    Intent intent = new Intent(MyApplicantion.getContext(),   AppSettingActivity.class);
    intent.putExtra("PackageName", AppId);
    MyApplicantion.getContext().startActivity(intent);
  • 需要对intent声明 FLAG_ACTIVITY_NEW_TASK
    1
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

备注

  • 网上的原因分析:
  • 因为standard模式的Activity默认会进入启动它的Activity所属的任务栈中,但是由于非Activity类型的context(ApplicationContext)并没有所谓的任务栈,所以就出现问题了。需要指定Activity为FLAG_ACTIVITY_NEW_TASK标记位,这样启动的时候,就会为它创建一个新的任务栈了。–android开发艺术探究


编程环境

  • Android Studio 3.0

问题

  • 横屏模式下,显示内容被遮挡

过程

  • Google😂

解决

  • ScrollView 只允许嵌套一个子布局.超出范围部分会自动增加滚动条

备注

  • ScrollView 只能添加竖直方向滚动条.


资料来源如下
给初学者的RxJava2.0教程(demo代码来源)

http://www.jianshu.com/u/c50b715ccaeb

编程环境

  • Android Studio 2.2.3

  • 在Gradle配置:
    1
    2
    compile 'io.reactivex.rxjava2:rxjava:2.0.1'
    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'

观察者模式

  • 在Eventbus中亦涉及了相关概念,比较简单.包括Observable(被观察者)、Observer(观察者)、subscribe().事件由Observable(被观察者)开始发出,通过subscribe()最终被传递到Observer(观察者).而整个过程中你是站在Observer(观察者)的位置,也就是事件的末尾,观察Observable(被观察者).

  • 上图

  • demo

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    Observable.create(new ObservableOnSubscribe<Integer>() {
    @Override
    public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
    emitter.onNext(1);
    emitter.onComplete();
    }
    }).subscribe(new Observer<Integer>() {
    @Override
    public void onSubscribe(Disposable d) {
    Log.d(TAG, "subscribe");
    }

    @Override
    public void onNext(Integer value) {
    Log.d(TAG, "" + value);
    }

    @Override
    public void onError(Throwable e) {
    Log.d(TAG, "error");
    }

    @Override
    public void onComplete() {
    Log.d(TAG, "complete");
    }
    });
  • 事件由emitter.onNext(1);开始,最终被public void onNext(Integer value)相应.被观察者事件发送结束调用emitter.onComplete();,同时观察者最终以public void onComplete()相应.


  • note: 上下游以.subscribe建立连接后,事件才会开始发送.

Observable(被观察者) Observer(观察者)

  • ObservableEmitter
    ObservableEmitter: Emitter意为发射器,事件发送.onNext(T value)、onComplete()和onError(Throwable error)分别对应next事件、complete事件和error事件。
  • Observer中onNext(Integer value)、onError(Throwable e)、onComplete()对应接受next事件、complete事件和error事件
  • 被观察者发送complete事件和error事件后,观察者接受后不再继续响应事件,即使被观察者还在发送事件.complete事件和error事件互斥.
  • 在Observer中,调用Disposable.dispose(),切断管道.被观察者继续发送,但观察者不再响应.

subscribe

  • 建立Observable(被观察者) Observer(观察者)之间的管道.有多个重载.
  • 重载
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public final Disposable subscribe() {}

    public final Disposable subscribe(Consumer<? super T> onNext) {}

    public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError) {}

    public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete) {}

    public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete, Consumer<? super Disposable> onSubscribe) {}

    public final void subscribe(Observer<? super T> observer) {}
  • 重载说明
    • 不带参数 : 观察者不关心任何事件(有卵用😵)
    • 只带onNext : 观察者只响应next事件.
    • 其他类似….演绎推理….
    • 最后一个是传入完整的Observer对象.(demo就是🙃)

操作符

  • 基于Rxjava的观察者模式可以拆分大多数的业务逻辑,即使再增加很多功能整体也不会过于混乱.
  • 但Rxjava的强大并不局限在拆分逻辑.由被观察者到观察者的整个事件传递过程,基于Rxjava我们可以任意拆分 合并 转换 事件、切换线程等.

  • note: 操作符搭配 Lambda 表达式食用更佳 🤣

创建

  • 产生并发送 Obserable 事件.
  • 仅常用,详细在 RxJava 2.x 使用详解(二) 创建操作符

    .creat

  • 前面demo中已经实际使用过了
  • 用于产生一个 Obserable 被观察者对象,demo如上所示.

.just

  • 对于简单的几个数据,直接使用just发送即可,无需创建 Obserable 对象.just最多可以接收 10 个参数.
  • demo
    1
    2
    Observable.just("test","test2")
    .subscribe(str -> Log.i("tag", str));
    相当于顺序调用onNext(“test”)和onNext(“test2”),最后调用onComplete方法。

.fromArray

  • 功能与just类似但fromArray来接收任意长度的数据数组,也可以直接传入数组fromArray(new int[]{1, 2, 3}) 
  • demo
    1
    2
    Observable.fromArray(1, 2, 3, 4, 5)
    .subscribe(integer -> Log.i("tag", String.valueOf(integer)));
    fromArray不支持直接传入list进,list会被当作一个整体发送.

.fromIterable

  • 功能与fromArray类似,但是可以接收 list 类型,遍历可迭代数据集合.
  • demo
    1
    2
    3
    4
    5
    6
    7
    8
    List<String> list = new ArrayList<>();
    list.add("a");
    list.add("b");
    list.add("c");

    Flowable.fromIterable(list).subscribe(
    s -> Log.i("tag", s)
    );

.timer

  • 指定一段时间间隔后发送数据(一次性),不太常用.

线程切换

  • 默认事件传递的双方在同一线程工作.

  • demo

    1
    2
    3
    4
    5
    observable.subscribeOn(Schedulers.newThread())
    .subscribeOn(Schedulers.io())
    .observeOn(AndroidSchedulers.mainThread())
    .observeOn(Schedulers.io())
    .subscribe(consumer);
  • 方法:

    • .subscribeOn() : 指定被观察者发送事件线程,仅第一次调用时有效!
    • .observeOn() : 指定观察者/流变换(对发送的事件处理🖖)线程,多次调用,多次切换有效
  • 参数:

    • Schedulers.io() : 适用于io密集型操作,网络通信、磁盘操作等.
    • Schedulers.computation() : CPU密集操作,需要大量CPU计算的操作.
    • Schedulers.newThread() : 创建新线程.
    • AndroidSchedulers.mainThread() : Android主线程,通常为更新UI等.

过滤

  • 对 Obserable 事件筛选.
  • 仅常用,详细在RxJava 2.x 使用详解(三) 过滤操作符

.filter

  • 基本过滤操作符,按照任意自定规则过滤.

组合

转换

.map

  • 处理前后事件数量之比1:1,事件变换前后顺序不变
  • map作用是对Observable发送的每一个事件,应用处理变换函数,再继续像下游发送.中间过程可以转换事件类型、改变事件内容等等.只需要变换后的事件类型与下游接收的类型匹配即可.
  • demo
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    Observable.create(new ObservableOnSubscribe<Integer>() {
    @Override
    public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
    emitter.onNext(1);
    emitter.onNext(2);
    emitter.onNext(3);
    }
    }).map(new Function<Integer, String>() {
    @Override
    public String apply(Integer integer) throws Exception {
    return "This is result " + integer;
    }
    }).subscribe(new Consumer<String>() {
    @Override
    public void accept(String s) throws Exception {
    Log.d(TAG, s);
    }
    });

    这里是把int类型转换为了string类型,与观察者接收的类型匹配即可.

.flatMap

  • 处理前后事件数量之比 1:n,事件变换前后顺序不保证
  • flatMap,通俗点就是把Observable发送的事件拆散变换再,继续像下游发送.1个Observable事件可拆成任意个.只需要变换后的事件类型与下游接收的类型匹配即可.
  • demo
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    Observable.create(new ObservableOnSubscribe<Integer>() {
    @Override
    public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
    emitter.onNext(1);
    emitter.onNext(2);
    emitter.onNext(3);
    }
    }).flatMap(new Function<Integer, ObservableSource<String>>() {
    @Override
    public ObservableSource<String> apply(Integer integer) throws Exception {
    final List<String> list = new ArrayList<>();
    for (int i = 0; i < 3; i++) {
    list.add("I am value " + integer);
    }
    return Observable.fromIterable(list).delay(10,TimeUnit.MILLISECONDS);
    }
    }).subscribe(new Consumer<String>() {
    @Override
    public void accept(String s) throws Exception {
    Log.d(TAG, s);
    }
    });

    这里是flatMap把一个int类型事件拆成了3个String类型,运行结果看,最终事件到达顺序与onNext(1);onNext(2);的发送顺序无关

.concatMap

  • 处理前后事件数量之比 1:n,事件变换前后顺序按顺序
  • 与flatMap作用相同,只是保证了事件严格按顺序达到下游.
  • demo 就不上了,直接替换flatMap的位置就好.

.zip

  • 处理前后事件数量之比 n:1,事件变换前后顺序严格按顺序
  • zip.最常见的压缩文件格式,在这里也是类似的意思,zip可以严格按照顺序合并多个 不同类型 Observable发送的事件.总的发送事件数量与上游Observable发送最少的那个数量相同.
  • demo
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    Observable<Integer> observable1 = Observable.create(new ObservableOnSubscribe<Integer>() {
    @Override
    public void subscribe(ObservableEmitter<Integer> emitter) throws Exception {
    Log.d(TAG, "emit 1");
    emitter.onNext(1);
    Thread.sleep(1000);

    Log.d(TAG, "emit 2");
    emitter.onNext(2);
    Thread.sleep(1000);

    Log.d(TAG, "emit 3");
    emitter.onNext(3);
    Thread.sleep(1000);

    Log.d(TAG, "emit 4");
    emitter.onNext(4);
    Thread.sleep(1000);

    Log.d(TAG, "emit complete1");
    emitter.onComplete();
    }
    }).subscribeOn(Schedulers.io());

    Observable<String> observable2 = Observable.create(new ObservableOnSubscribe<String>() {
    @Override public void subscribe(ObservableEmitter<String> emitter) throws Exception {
    Log.d(TAG, "emit A");
    emitter.onNext("A");
    Thread.sleep(1000);

    Log.d(TAG, "emit B");
    emitter.onNext("B");
    Thread.sleep(1000);

    Log.d(TAG, "emit C");
    emitter.onNext("C");
    Thread.sleep(1000);

    Log.d(TAG, "emit complete2");
    emitter.onComplete();
    }
    }).subscribeOn(Schedulers.io());

    Observable.zip(observable1, observable2, new BiFunction<Integer, String, String>() {
    @Override
    public String apply(Integer integer, String s) throws Exception {
    return integer + s;
    }
    }).subscribe(new Observer<String>() {
    @Override
    public void onSubscribe(Disposable d) {
    Log.d(TAG, "onSubscribe");
    }

    @Override
    public void onNext(String value) {
    Log.d(TAG, "onNext: " + value);
    }

    @Override
    public void onError(Throwable e) {
    Log.d(TAG, "onError");
    }

    @Override
    public void onComplete() {
    Log.d(TAG, "onComplete");
    }
    });
    这里两个事件发送在同一线程中.当两个事件发送不再同一线程时,情况类似,不过当异步时,数量较少的事件发送完成,发送Complete事件后,通道随即被切断.

.Concat

  • 处理前后事件数量之比 n:1,事件变换前后顺序严格按顺序
  • Concat,可以严格按照顺序合并 相同类型 Observable发送的事件.

Backpressure


  • 被翻译为背压…(如此文不达意的直译,能忍?往下都是因为原文..😈)

  • 其实概念有够简单:将整个事件产生/传递/处理的过程想象为一条河流由上而下, Backpressure 指的是上游产生的事件太快,远远超过了下游的处理速度,以至于缓冲区溢出.上游来了洪水,下游径流量不够,以至于中间河道跨过了堤岸,溢出.

Flowable基础

  • Rxjava 1.x中需要自行通过操作符处理,到了2.0中,则有了专门对付发洪水上游的被观察者- Flowable .我们常用的 observable 在2.x中一般用于不涉及 Backpressure 的情况.而对应与 observable 的 Observer ,改为了 Subscriber .

  • demo

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    Flowable<Integer> upstream = Flowable.create(new FlowableOnSubscribe<Integer>() {
    @Override
    public void subscribe(FlowableEmitter<Integer> emitter) throws Exception {
    Log.d(TAG, "emit 1");
    emitter.onNext(1);
    Log.d(TAG, "emit 2");
    emitter.onNext(2);
    Log.d(TAG, "emit 3");
    emitter.onNext(3);
    Log.d(TAG, "emit complete");
    emitter.onComplete();
    }
    }, BackpressureStrategy.ERROR); //增加了一个参数

    Subscriber<Integer> downstream = new Subscriber<Integer>() {

    @Override
    public void onSubscribe(Subscription s) {
    Log.d(TAG, "onSubscribe");
    s.request(Long.MAX_VALUE); //注意这句代码
    }

    @Override
    public void onNext(Integer integer) {
    Log.d(TAG, "onNext: " + integer);

    }

    @Override
    public void onError(Throwable t) {
    Log.w(TAG, "onError: ", t);
    }

    @Override
    public void onComplete() {
    Log.d(TAG, "onComplete");
    }
    };

    upstream.subscribe(downstream);
  • 注意两个地方

    • Flowable创建时比 observable 多了一个参数.(参数作用下节说明)
    • Subscriber中调用了 s.request .
  • Flowable 与 Observable 最大的不同就是 Flowable再次发送事件需要等待 Subscriber 中调用 .request

  • .request() 实质上是下游告知上游自己的处理能力,使得上游根据下游处理能力发送事件.多次调用,上游表示处理能力的数字会叠加,上游每发送一个事件,该数字减一,到0抛出异常

    • 上下游在同一线程时,下游没有或没有及时调用 .request ,上游会抛出异常
    • 异步线程时,下游即使没有调用 .request 会有128个事件的缓存区.上游可继续发出事件,缓存区超出128个事件后,抛出异常.

Flowable拓展

  • 这里对 Flowable 多的参数进行说明.

  • 参数
    • BackpressureStrategy.BUFFER : 默认缓存区128,这个参数极大拓展了缓存区,使得 Flowable 表现与 Observable 差不多.
    • BackpressureStrategy.DROP : 128缓存区满了,就丢弃上游事件,直到下游处理了一个事件,缓存区 -1 ,再允许存入新的上游事件.
    • BackpressureStrategy.LATEST : 永远保存最后达到的128个上游事件,上游有新的事件到达满载的缓存区时,丢弃第一个存入缓存区的上游事件.

  • 对于不是由我们编写的 Flowable 也可以通过 interval 操作符来加工.
    1
    2
    Flowable.interval(1, TimeUnit.MICROSECONDS)
    .onBackpressureDrop() //加上 Backpressure 策略
  • 对应上文,指定参数有3,意思同上.
    • onBackpressureBuffer()
    • onBackpressureDrop()
    • onBackpressureLatest()


编程环境

  • Android Studio 3.0

问题

  • Glide是一个通用的图片缓存框架,但是在MyPrivacy显示appIcon时,传入 一个Drawable对象,提示类型不匹配.

  • (注意: 这里是直接传入Drawable对象,不是经过 R.xx 引用 !)

过程

  • 查阅资料后,确认Glide不支持直接加载传入的Drawable对象,转换为bitDrawable类型也不可.

  • 解决思路来自

    https://github.com/bumptech/glide/issues/588

  • 不支持直接加载,但Glide的.error(Icon)错误时显示 .placeholder(Icon)占位符,支持Drawable对象

解决

  • 不再直接加载 .load 传入空字符串, 通过 .placeholder 简洁加载.
  • demo
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Drawable Icon = xxx;

    RequestOptions options = new RequestOptions()
    .error(Icon)
    .placeholder(Icon);

    Glide.with(context)
    .load("")
    .apply(options)
    .into(imageView);

    备注

  • placeholder 在Glide 4.x版本中,移入了 RequestOptions 对象中.


  • ubuntu16.04
  • 使用busybox编译最小文件系统,使用qemu运行起来。
  • 内容来自 奔跑吧linux内核第6章
  • 这里将输入代码过程集合到了几个.sh文件,不做重复的工作 !
  • 当然网好是前提,最好挂代理.

安装工具

  • 首先需要安装qemu gcc, ubuntu16.04中自带的gcc版本较低,这里我们安装书中推荐的gcc-arm-linux-gnueabi

    1
    sudo apt-get install qemu libncurses5-dev gcc-arm-linux-gnueabi build-essential
  • 下载busybox源码

    • 书中推荐版本是1.24,但最新版本已经到了busybox-1.27.2.这里我们使用最新版

      1
      wget https://busybox.net/downloads/busybox-1.27.2.tar.bz2
    • 解压到 busybox 文件夹

      1
      tar -jxvf busybox-1.27.2.tar.bz2
  • 下载linux内核源码

    • 还是以配套的4.0源码为例,(提醒:内核解压后大约占800MB,请预留出足够空间)

      1
      wget https://www.kernel.org/pub/linux/kernel/v4.x/linux-4.0.tar.gz
    • 解压到linux文件夹

      1
      tar -jxvf linux-4.0.tar.gz

编译最小文件系统

  • 别问我最小文件系统是什么,我也有点😵,但是先用起来.

  • 首先利用 busybox 手工编译一个最小文件系统。
    在busybox文件夹下

    1
    2
    3
    4
    export ARCH=ARM
    export CROSS_COMPILE=arm-linux-gnueabi-
    make menuconfig
    make install
  • 进入menuconfig后,配置静态编译

    1
    2
    3
    Busybox Settings --->
    Build Options --->
    [*] Build BusyBox as a static binary (no shared libs)
  • 然后 make install 编译完成。编译完成后,把 busybox 根目录下面的_install 目录拷贝到 linux-4.0 下。

  • 进入_install 目录,创建 etc、dev 等目录。

    1
    2
    3
    4
    mkdir etc
    mkdir dev
    mkdir mnt
    mkdir -p etc/init.d/
  • 在_install /etc/init.d/目录下创建 文件名rcS 的文件,写入以下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    mkdir –p /proc
    mkdir –p /tmp
    mkdir -p /sys
    mkdir –p /mnt
    /bin/mount -a
    mkdir -p /dev/pts
    mount -t devpts devpts /dev/pts
    echo /sbin/mdev > /proc/sys/kernel/hotplug
    mdev –s

    同时使用 chmod +x rcS修改rcS的可执行权限.

  • 在_install /etc 目录创建文件名 fstab 的文件,并写入以下内容。

    1
    2
    3
    4
    5
    proc /proc proc defaults 0 0
    tmpfs /tmp tmpfs defaults 0 0
    sysfs /sys sysfs defaults 0 0
    tmpfs /dev tmpfs defaults 0 0
    debugfs /sys/kernel/debug debugfs defaults 0 0
  • 在_install /etc 目录创建文件名 inittab 的文件,并写入如下内容。

    1
    2
    3
    4
    ::sysinit:/etc/init.d/rcS
    ::respawn:-/bin/sh
    ::askfirst:-/bin/sh
    ::ctrlaltdel:/bin/umount -a –r
  • 在_install/dev 目录下创建如下设备节点,以root权限执行

    1
    2
    3
    cd _install/dev/
    sudo mknod console c 5 1
    sudo mknod null c 1 3

.sh(配合chmod +x使用)

  • build.sh: 编译busybox

    1
    2
    3
    4
    export ARCH=ARM
    export CROSS_COMPILE=arm-linux-gnueabi-
    make menuconfig
    make install
  • creat.sh: _install文件夹下处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    rm -rf etc
    rm -rf dev
    rm -rf mnt
    mkdir etc
    mkdir dev
    mkdir mnt
    mkdir -p etc/init.d/

    echo "mkdir -p /proc
    mkdir -p /tmp
    mkdir -p /sys
    mkdir -p /mnt
    /bin/mount -a
    mkdir -p /dev/pts
    mount -t devpts devpts /dev/pts
    echo /sbin/mdev > /proc/sys/kernel/hotplug
    mdev -s
    " > etc/init.d/rcS
    chmod +x etc/init.d/rcS

    echo "proc /proc proc defaults 0 0
    tmpfs /tmp tmpfs defaults 0 0
    sysfs /sys sysfs defaults 0 0
    tmpfs /dev tmpfs defaults 0 0
    debugfs /sys/kernel/debug debugfs defaults 0 0
    " > etc/fstab

    echo "::sysinit:/etc/init.d/rcS
    ::respawn:-/bin/sh
    ::askfirst:-/bin/sh
    ::ctrlaltdel:/bin/umount -a -r
    " > etc/inittab

    cd dev/
    sudo mknod console c 5 1
    sudo mknod null c 1 3

编译内核

  • 编译内核

    1
    2
    3
    4
    5
    cd linux-4.0
    export ARCH=arm
    export CROSS_COMPILE=arm-linux-gnueabi-
    make vexpress_defconfig
    make menuconfig
  • 配置 initramfs,在 initramfs source file 中填入_install。另外需要把 Default kernel command string 清空。

    1
    2
    3
    4
    5
    General setup --->
    [*] Initial RAM filesystem and RAM disk (initramfs/initrd) support
    (_install) Initramfs source file(s)
    Boot options -->
    ()Default kernel command string
  • 配置 memory split 为“3G/1G user/kernel split”以及打开高端内存。

    1
    2
    3
    Kernel Features --->
    Memory split (3G/1G user/kernel split) --->
    [ *] High Memory Support
  • 开始编译 kernel

    1
    2
    make bzImage -j4 ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
    make dtbs
  • 运行 QEMU 来模拟 4 核 Cortex-A9 的 Versatile Express 开发平台。

    1
    qemu-system-arm -M vexpress-a9 -smp 4 -m 1024M -kernel arch/arm/boot/zImage -append "rdinit=/linuxrc console=ttyAMA0 loglevel=8" -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographic

.sh(配合chmod +x)

  • build.sh : 编译内核

    1
    2
    3
    4
    5
    6
    export ARCH=arm
    export CROSS_COMPILE=arm-linux-gnueabi-
    make vexpress_defconfig
    make menuconfig
    make bzImage -j4 ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
    make dtbs
  • run.sh : 运行arm内核

    1
    qemu-system-arm -M vexpress-a9 -smp 4 -m 1024M -kernel arch/arm/boot/zImage -append "rdinit=/linuxrc console=ttyAMA0 loglevel=8" -dtb arch/arm/boot/dts/vexpress-v2p-ca9.dtb -nographicyun

  • wdt第二弹,内核API,勉强翻译下来了,还需要进一步整理

看门狗定时器驱动内核API

  • 最后更新时间 2013.2.12
  • Wim Van Sebroeck wim@iguana.be

介绍

  • 本文档没有描述看门狗设备或驱动。也不涉及那些在用户空间调用的API,对应在 Documentation/watchdog/watchdog-api.txt
  • 本文档描述的是,利用看门狗子系统框架方式进行看门狗驱动编写时所使用到的API。看门狗子系统框架提供了所有与用户空间交互的接口,不需要每次编写重复的代码。这意味这驱动程序只需要提供几个不同的操作函数,就可以控制WDT了。

API

  • 每一个想要使用看门狗核心子系统的驱动程序都必须包含#include<linux/watchdog.h>(编写驱动程序是不得不做的工作)。linux/watchdog.h包含以下两个注册/卸载函数:

  • ```shell
    extern int watchdog_register_device(struct watchdog_device *);
    extern void watchdog_unregister_device(struct watchdog_device *);

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31

    * 注册函数将wdt设备注册进wdt子系统,函数的参数是一个指向struct watchdog_device的结构体_。当注册成功时返回0.注册失败返回一个负值。
    * 卸载函数是将wdt设备从wdt子系统卸载,函数参数是一个指向struct watchdog_device的结构体_
    * 看门子系统包含了注册时间调整机制(原文是 an registration deferral mechanism 一种延期机制?上下文不太对啊),允许在开机阶段,你可以按照你设定的尽可早的注册一个看门狗设备。

    ### struct watchdog_device_

    * 这里粘贴的是linux4.1里定义的watchdog_device,原文的watchdog_device更长但跟源码对不起来,所以这里就以内核4.1里定义的为准了😑:

    ```c
    struct watchdog_device {
    int id;
    struct cdev cdev;
    struct device *dev;
    struct device *parent;
    const struct watchdog_info *info;
    const struct watchdog_ops *ops;
    unsigned int bootstatus;
    unsigned int timeout;
    unsigned int min_timeout;
    unsigned int max_timeout;
    void *driver_data;
    struct mutex lock;
    unsigned long status;
    /* Bit numbers for status flags */
    #define WDOG_ACTIVE 0 /* Is the watchdog running/active */
    #define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */
    #define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */
    #define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */
    #define WDOG_UNREGISTERED 4 /* Has the device been unregistered */
    };
  • 包含以下字段:

    • id:由watchdog_register_device函数注册。id 0是一个特殊的,id 0 有/dev/watchdog0 cdev(动态主设备号,次设备号0) 和 /dev/watchdog miscdev。id 在调用到watchdog_register_device时自动注册。
      • NOTE看源码,wdt子系统是基于misc子系统的,注册wdt设备调用的是misc_register(&watchdog_miscdev);而misc设备主设备号只能为10,这里结论有冲突,等待解决中。。。
    • parent:在调用watchdog_register_device函数前,设置父设备(或者设置为null)
    • info:指向watchdog_info结构体的指针,watchdog_info提供了看门狗本身的一些附加信息(像是看门狗独有的名称之类的)
    • ops:指向watchdog_ops结构体的指针,watchdog_ops是看门狗支持操作(函数)的集合。
    • bootstatus:启动后设备状态(与看门狗WDIOF_* 标志位一同开启)_
    • timeout:看门狗超时的时间(秒为单位).如果设置了WDOG_ACTIVE_启用了看门狗,在这个时间长度后,用户空间还没有发送心跳包,看门狗会将系统复位重启.
    • min_timeout:_可设置的看门狗最小超时时间
    • max_timeout:_可设置的看门狗最大超时时间
    • driver_data:_指向看门狗设备私有数据的指针,这个data只能由watchdog_set_drvdata 和 watchdog_get_drvdata routines函数访问.
    • struct mutex lock; 原文档没有🙄
    • status:这个字段包含了许多状态位,提供有关设备的额外信息(例如:看门狗的活动状态\限制,现在nowayout 位设置与否)(括号里翻译是否准确存疑)

struct watchdog_ops

  • watchdog_ops_

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    struct watchdog_ops {
    struct module *owner;
    /* mandatory operations */
    int (*start)(struct watchdog_device *);
    int (*stop)(struct watchdog_device *);
    /* optional operations */
    int (*ping)(struct watchdog_device *);
    unsigned int (*status)(struct watchdog_device *);
    int (*set_timeout)(struct watchdog_device *, unsigned int);
    unsigned int (*get_timeleft)(struct watchdog_device *);
    void (*ref)(struct watchdog_device *);
    void (*unref)(struct watchdog_device *);
    long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
    };
  • 你一开始定义的看门狗的module owner是非常重要的,module owner 在使用看门狗时会同时锁定看门狗设备.(这样避免了在卸载模块时/dev/watchdog依然是打开状态引起的系统崩溃)

  • 某些函数是必须实现的,其他是可选的,必须实现的函数如下:

    • *start :*指向看门狗设备启动函数的指针.这个函数参数为一个 struct watchdog_device,_成功返回0,失败返回负数.
    • *stop :*通过这个函数关闭看门狗设备.这个函数参数为一个 struct watchdog_device,_成功返回0,失败返回负数.
      • 一些硬件看门狗设备只能启动,没有关闭选项.对应这些设备的驱动,不用实现 *stop ,*如果驱动程序没有关闭设备功能,看门狗核心层会在设备关闭后设置 WDOG_HW_RUNNING 并调用驱动程序的 keepalive pings功能.
      • 如果看门狗驱动没有实现stop必须设置max_hw_heartbeat_ms_
  • 不是所有的硬件看门狗都有相同的功能,这就是为什么会有可选函数了,只有但这项功能被硬件看门🐶支持时,才编写对应函数.

    • ping:这是看门狗驱动实现的喂狗函数,这个函数的参数时一个指向struct watchdog_device的指针_.函数执行成功返回0,失败返回负数.
      • 大多数的硬件不支持直接喂狗的特殊功能,一般是直接重启硬件看门狗.
      • 看门狗子系统的核心层也是这样做的:但定期喂狗的操作转发到硬件看门狗时,核心层在 ping 实现时调用喂狗函数,但喂狗函数没有实现时,使用 start 对应函数
      • ( WDIOC_KEEPALIVE对应的 ioctl命令只有在i看门狗info里的WDIOF_KEEPALIVEPING被设置为1后才会生效 )
    • status: 这个函数用来检查看门狗设备的状态,通过看门狗的WDIOF_*_状态标志位报告.WDIOF_MAGICCLOSE and WDIOF_KEEPALIVEPING由看门狗核心层报告.没有必要在驱动程序中报告.此外如果驱动程序没有实现该函数,看门狗核心层会返回 struct watchdog_device_中的bootstatus状态位.
    • set_timeout:这个函数设置和检查 超时时间的变化,返回0表示成功,返回-EINVAL表示超出范围,返回-EIO表示无法写入看门狗.设置成功后,函数应该将设定的超时时间写入看门狗.(或许与通过接口获取的超时时间不同,因为看门狗的分辨率不一定能到1s).
      • 实现了max_hw_heartbeat_ms的驱动将硬件看门狗心跳值设置为超时时间和max_hw_heartbeat_ms之间的最小值.Those drivers set the timeout value of the watchdog_device either to the requested timeout value (if it is larger than max_hw_heartbeat_ms), or to the achieved timeout value.
      • 如果看门狗驱动程序没有执行任何操作,但设置了watchdog_device.timeout_,此回掉函数忽略.
      • 如果没有设置 set_timeout,但设置了WDIOF_SETTIMEOUT,看门狗架构会将 watchdog_device中timeout值更新为请求的值.
      • 如果使用了pretimeout feature (WDIOF_PRETIMEOUT),那么set_timeout 必须同时检查 pretimeout 是否有效 ,设置相应定时器. 这些操作在核心层没有races无法完成,所以必须在驱动中完成.
    • set_pretimeout_:这个函数支持检查/改变看门狗中pretimeout值(pretimeout详情见wdt用户层api翻译).这个函数是可选支持的,因为不是所有的看门狗都支持这个功能.pretimeout 不是绝对时间,其数值是在超时时间之前几秒发生.
      • 返回0表示执行成功,返回-EINVAL,表示超出范围,返回-EIO表示未能成功向看门狗写入.
      • 设置pretimeou为0值,代表禁用pretimeout功能.(WDIOF_PRETIMEOUT_需要在看门狗的info 结构体中设置)
      • 如果驱动没有实现任何功能,但设定了 watchdog_device.pretimeout_,此回掉函数忽略.这意味这,如果没有提供set_pretimeout_函数,但设定了WDIOF_PRETIMEOUT_,看门狗架构会将pretimeout的值更新为请求值.
    • get_timeleft:_这个函数返回复位前剩余时间.
    • restart:此函数重启看门狗硬件,返回0表示成功,失败返回对应错误码.
    • ioctl:如果这个函数存在,在系统进行内部ioctl命令处理前,会首先调用此函数.如果不支持ioctrl命令,应该返回 -ENOIOCTLCMD .这个函数的参数为(watchdog_device, cmd and arg)_
    • ref和unref 已经不再被支持.
  • The status bits should (preferably) be set with the set_bit and clear_bit alike bit-operations. The status bits that are defined are:

    • WDOG_ACTIVE:_这个状态位标识着从用户空间看一个看门狗设备是否被激活,如果被激活,用户空间需要执行喂狗动作

    • WDOG_NO_WAY_OUT:_这个状态位标识这nowayout的设置,如果被激活,那看门狗启动后,无法被停止.

    • WDOG_HW_RUNNING:如果硬件看门狗运行,此状态位被置1.

      • 当硬件看门狗不能被停止时,这个状态位必须被置1.
      • 如果系统启动后自动启动看门狗,在看门狗打开前,必须设置此状态位.
      • 当此状态位被置1,即使WDOG_ACTIVE为0_.看门狗架构也会执行喂狗动作.
      • (当你直接注册设置了 WDOG_HW_RUNNING位的看门狗设备时,执行open /dev/watchdog动作,系统会直接跳过启动操作,直接执行喂狗操作 )
    • 要设置WDOG_NO_WAY_OUT_状态位(在注册看门狗设备前)你可以:

      • 在watchdog_device_的结构体中设置

        1
        .status = WATCHDOG_NOWAYOUT_INIT_STATUS,

        这样等同于将WDOG_NO_WAY_OUT_值设置为CONFIG_WATCHDOG_NOWAYOUT

      • 使用下面的函数

        1
        static inline void watchdog_set_nowayout(struct watchdog_device *wdd, int nowayout)
  • (note:) wdt驱动程序核心支持magic close和 nowayout功能.要使用magic close,需要在看门狗的 info中设置WDIOF_MAGICCLOSE_位.开启 nowayout 会覆盖magic close.

  • 获取或设定驱动程序特殊数据,应该使用以下两个函数:

    1
    2
    static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data)
    static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)
    • watchdog_set_drvdata函数允许你添加向驱动程序添加特殊数据,此函数的参数为指向看门狗设备的指针,和指向需要添加数据的指针.
    • watchdog_get_drvdata函数运行你读取驱动程序中的特殊数据,此函数的参数为指向你想读取的看门狗设备的指针.
  • 初始化 timeout ,会用到下面的函数

    1
    extern int watchdog_init_timeout(struct watchdog_device *wdd,unsigned int timeout_parm, struct device *dev);
    • watchdog_init_timeout允许使用模块的 timeout 字段初始化 timeout ,当模块的 timeout 字段无效时,设备树中的timeout-sec也可.最好的做法是将watchdog_device_中timeout的值,设置为初始默认值,然后使用到函数的用户可以分别设置自定义值.
  • 重启后禁用看门狗,需要使用以下函数:

    1
    static inline void watchdog_stop_on_reboot(struct watchdog_device *wdd);
  • 卸载看门狗设备时禁用看门狗,必须调用此函数,如果 nowayout 没有设置,这个函数只会停止看门狗.

    1
    static inline void watchdog_stop_on_unregister(struct watchdog_device *wdd);
  • 更改重启处理程序的优先级,(猜测是不同看门狗的优先级)调用下面的函数:

    1
    oid watchdog_set_restart_priority(struct watchdog_device *wdd, int priority);
    • 设定优先级时,用户需要遵循以下规则
      • 0:优先级最低,非常有限的重启能力
      • 128:重启处理的默认选择,如果没有其他的重启处理程序可用,或者没有重启整个系统的重启处理程序可用.
      • 255:最高优先级,会覆盖其他所有重启处理程序
  • 使用pretimeout 通知功能,需要利用以下函数:

    1
    void watchdog_notify_pretimeout(struct watchdog_device *wdd)
    • 这个函数可以在中断上下文中调用,如果启用了watchdog pretimeout governor 框架(kbuild CONFIG_WATCHDOG_PRETIMEOUT_GOV),就会采用进入驱动程序分配好的处理程序,如果没有启用watchdog pretimeout governor 框架,watchdog_notify_pretimeout()会将信息输出到内核日志缓存.