转自:https://blog.csdn.net/gatieme/article/details/73715860

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/gatieme/article/details/73715860
本文信息

CSDN GitHub
Linux Kernel PANIC(三)–Soft Panic/Oops调试及实例分析 LDD-LinuxDeviceDrivers/study/debug/modules/panic/03-soft_panic
同类博文信息

CSDN GitHub
Linux Kernel PANIC(一)–概述(Hard Panic/Aieee和Soft Panic/Oops) LDD-LinuxDeviceDrivers/study/debug/modules/panic/01-kernel_panic
Linux Kernel PANIC(二)–Hard Panic/Aieee实例分析 LDD-LinuxDeviceDrivers/study/debug/modules/panic/02-hard_panic
Linux Kernel PANIC(三)–Soft Panic/Oops调试及实例分析 LDD-LinuxDeviceDrivers/study/debug/modules/panic/03-soft_panic

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可, 转载请注明出处

凡是非中断处理引发的模块崩溃都将导致 soft panic

在这种情况下, 驱动本身会崩溃, 但是还不至于让系统出现致命性失败, 因为它没有锁定中断处理例程. 导致 hard panic的原因同样对soft panic也有用(比如在运行时访问一个空指针).

1 驱动OOPS实例分析
1.1 导致 OOPS 的代码
模块代码, 有一处 NULL 指针异常

// http://blog.csdn.net/tommy_wxie/article/details/12521535
// http://blog.chinaunix.net/uid-20651662-id-1906954.html
// kerneloops.c
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>

static int __init hello_init(void)
{
int *p = 0;

*p = 1;

return 0;
}

static void __exit hello_exit(void)
{
return;
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");
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
makefile

# -------------------------------------------------
#
# Makefile for the LDD-LinuxDeviceDrivers.
#
# Author: gatieme
# Create: 2016-07-29 15:50:46
# Last modified: 2016-07-29 16:10:29
# Description:
# This program is loaded as a kernel(v2.6.18 or later) module.
# Use "make install" to load it into kernel.
# Use "make remove" to remove the module out of kernel.
#
# -------------------------------------------------

# my driver description
DRIVER_VERSION := "1.0.0"
DRIVER_AUTHOR := "Gatieme @ AderStep Inc..."
DRIVER_DESC := "Linux input module for Elo MultiTouch(MT) devices"
DRIVER_LICENSE := "Dual BSD/GPL"

MODULE_NAME := kerneloops
EXTRA_CFLAGS += -g

ifneq ($(KERNELRELEASE),)

obj-m := $(MODULE_NAME).o #print_vmarea.o

else

KERNELDIR ?= /lib/modules/$(shell uname -r)/build

PWD := $(shell pwd)

modules:
make -C $(KERNELDIR) M=$(PWD) modules

modules_install:
make -C $(KERNELDIR) M=$(PWD) modules_install

insmod:
sudo insmod $(MODULE_NAME).ko

reinsmod:
sudo rmmod $(MODULE_NAME)
sudo insmod $(MODULE_NAME).ko

rmmod:
sudo rmmod $(MODULE_NAME)

clean:
make -C $(KERNELDIR) M=$(PWD) clean
rm -f modules.order Module.symvers Module.markers

.PHNOY:
modules modules_install clean

endif
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
1.2 重现 OOPS
make后加载模块, 提示加载失败, 此时内核倒是了OOPS, 由于故障不严重, 系统并未死机

1.3 OOPS 信息
查看 Kernel 的日志, 或者 dmesg 打印日志可以查看 OOPS 信息

[ 5235.513513] BUG: unable to handle kernel NULL pointer dereference at (null)
[ 5235.513604] IP: [<ffffffffc0008003>] hello_init+0x3/0x1000 [kerneloops]
[ 5235.513671] PGD 0
[ 5235.513696] Oops: 0002 [#1] SMP
[ 5235.513736] Modules linked in: kerneloops(OE+) bbswitch(OE) cuse arc4 ath9k ath9k_common ath9k_hw uvcvideo videobuf2_vmalloc videobuf2_memops videobuf2_v4l2 videobuf2_core v4l2_common videodev i915 ath mac80211 rfcomm bnep media bluetooth cfg80211 intel_rapl x86_pkg_temp_thermal intel_powerclamp snd_hda_codec_hdmi kvm_intel snd_hda_codec_realtek drm_kms_helper kvm snd_hda_codec_generic snd_hda_intel snd_hda_codec snd_hda_core snd_hwdep drm snd_pcm acer_wmi sparse_keymap snd_seq_midi snd_seq_midi_event snd_rawmidi snd_seq snd_seq_device snd_timer snd mei_me mei irqbypass crct10dif_pclmul crc32_pclmul ghash_clmulni_intel aesni_intel i2c_algo_bit fb_sys_fops syscopyarea sysfillrect sysimgblt lpc_ich shpchp soundcore aes_x86_64 lrw gf128mul glue_helper ablk_helper cryptd nfsd joydev input_leds auth_rpcgss nfs_acl nfs serio_raw video mac_hid wmi lockd parport_pc ppdev coretemp grace sunrpc lp fscache parport binfmt_misc hid_generic psmouse pata_acpi usbhid tg3 hid sdhci_pci ptp sdhci pps_core fjes
[ 5235.514835] CPU: 1 PID: 9087 Comm: insmod Tainted: G OE 4.4.0-72-generic #93~14.04.1-Ubuntu
[ 5235.514918] Hardware name: Acer Aspire 4752/Aspire 4752, BIOS V2.10 08/25/2011
[ 5235.514984] task: ffff88013c5e6200 ti: ffff880050050000 task.ti: ffff880050050000
[ 5235.515050] RIP: 0010:[<ffffffffc0008003>] [<ffffffffc0008003>] hello_init+0x3/0x1000 [kerneloops]
[ 5235.515138] RSP: 0018:ffff880050053cc0 EFLAGS: 00010246
[ 5235.515187] RAX: 0000000000000000 RBX: ffffffff81e13080 RCX: 0000000000099cf4
[ 5235.515249] RDX: 0000000000099cf3 RSI: 0000000000000017 RDI: ffff8801a9003c00
[ 5235.515312] RBP: ffff880050053d38 R08: 000000000001a0a0 R09: ffffffff81002131
[ 5235.515374] R10: ffff8801afa5a0a0 R11: ffffea0004f13b80 R12: ffff88013c4eef00
[ 5235.515438] R13: 0000000000000000 R14: ffffffffc0008000 R15: ffff880050053eb0
[ 5235.515504] FS: 00002b2f9a71fb80(0000) GS:ffff8801afa40000(0000) knlGS:0000000000000000
[ 5235.515574] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 5235.515624] CR2: 0000000000000000 CR3: 000000005166b000 CR4: 00000000000406e0
[ 5235.515687] Stack:
[ 5235.515711] ffff880050053d38 ffffffff8100213d ffff880050053eb0 ffff880050053d10
[ 5235.515842] 0000000000000246 000000000000002e ffffffff811de97d ffff8801a9003c00
[ 5235.515949] ffffffff81183c84 0000000000000018 000000000da6966e ffffffffc05fe000
[ 5235.516085] Call Trace:
[ 5235.516127] [<ffffffff8100213d>] ? do_one_initcall+0xcd/0x1f0
[ 5235.516185] [<ffffffff811de97d>] ? kmem_cache_alloc_trace+0x1ad/0x220
[ 5235.516222] [<ffffffff81183c84>] ? do_init_module+0x27/0x1d2
[ 5235.516283] [<ffffffff81183cbd>] do_init_module+0x60/0x1d2
[ 5235.516346] [<ffffffff81104104>] load_module+0x1424/0x1b10
[ 5235.516433] [<ffffffff811008f0>] ? __symbol_put+0x40/0x40
[ 5235.516521] [<ffffffff81206a61>] ? kernel_read+0x41/0x60
[ 5235.516610] [<ffffffff811049be>] SYSC_finit_module+0x7e/0xa0
[ 5235.516706] [<ffffffff811049fe>] SyS_finit_module+0xe/0x10
[ 5235.516802] [<ffffffff81806eb6>] entry_SYSCALL_64_fastpath+0x16/0x75
[ 5235.516883] Code: <c7> 04 25 00 00 00 00 01 00 00 00 48 89 e5 5d c3 00 00 00 00 00 00
[ 5235.517020] RIP [<ffffffffc0008003>] hello_init+0x3/0x1000 [kerneloops]
[ 5235.517084] RSP <ffff880050053cc0>
[ 5235.517117] CR2: 0000000000000000
[ 5235.528875] ---[ end trace 69ea8d586c904d41 ]---
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
1.4 OOOPS信息分析
Oops: 0002 [#1] SMP
1
这个是 OOPS 信息的错误码

bit 描述
bit 0 0 means no page found, 1 means a protection fault
bit 1 0 means read, 1 means write
bit 2 0 means kernel, 1 means user-mode
[#1] — this value is the number of times the Oops occurred. Multiple Oops can be triggered as a cascading effect of the first one.
这个值是 Oops 发生的次数, 多个 Oops 可以级联效应触发

[ 5235.514835] CPU: 1 PID: 9087 Comm: insmod Tainted: G OE 4.4.0-72-generic #93~14.04.1-Ubuntu
1
表示这个 OOPS 发生在 CPU1, 当前运行的进程是9087号进程 insmod, Tainted 标识为 G, 内核版本是 4.4.0-72-generic, 操作系统为 #93~14.04.1-Ubuntu

其中Tainted的表示可以从内核中 kernel/panic.c 中找到

Tainted 描述
‘G’ if all modules loaded have a GPL or compatible license
‘P’ if any proprietary module has been loaded. Modules without a MODULE_LICENSE or with a MODULE_LICENSE that is not recognised by insmod as GPL compatible are assumed to be proprietary.
‘F’ if any module was force loaded by “insmod -f”.
‘S’ if the Oops occurred on an SMP kernel running on hardware that hasn’t been certified as safe to run multiprocessor. Currently this occurs only on various Athlons that are not SMP capable.
‘R’ if a module was force unloaded by “rmmod -f”.
‘M’ if any processor has reported a Machine Check Exception.
‘B’ if a page-release function has found a bad page reference or some unexpected page flags.
‘U’ if a user or user application specifically requested that the Tainted flag be set.
‘D’ if the kernel has died recently, i.e. there was an OOPS or BUG.
‘W’ if a warning has previously been issued by the kernel.
‘C’ if a staging module / driver has been loaded.
‘I’ if the kernel is working around a sever bug in the platform’s firmware (BIOS or similar).
然后是其中关键的几句

[ 5235.513604] IP: [<ffffffffc0008003>] hello_init+0x3/0x1000 [kerneloops]
1
接着是 OOPS 发生时, CPU 寄存器的信息

[ 5235.514984] task: ffff88013c5e6200 ti: ffff880050050000 task.ti: ffff880050050000
[ 5235.515050] RIP: 0010:[<ffffffffc0008003>] [<ffffffffc0008003>] hello_init+0x3/0x1000 [kerneloops]
[ 5235.515138] RSP: 0018:ffff880050053cc0 EFLAGS: 00010246
[ 5235.515187] RAX: 0000000000000000 RBX: ffffffff81e13080 RCX: 0000000000099cf4
[ 5235.515249] RDX: 0000000000099cf3 RSI: 0000000000000017 RDI: ffff8801a9003c00
[ 5235.515312] RBP: ffff880050053d38 R08: 000000000001a0a0 R09: ffffffff81002131
[ 5235.515374] R10: ffff8801afa5a0a0 R11: ffffea0004f13b80 R12: ffff88013c4eef00
[ 5235.515438] R13: 0000000000000000 R14: ffffffffc0008000 R15: ffff880050053eb0
[ 5235.515504] FS: 00002b2f9a71fb80(0000) GS:ffff8801afa40000(0000) knlGS:0000000000000000
[ 5235.515574] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 5235.515624] CR2: 0000000000000000 CR3: 000000005166b000 CR4: 00000000000406e0
1
2
3
4
5
6
7
8
9
10
11
接着是堆栈信息

[ 5235.515687] Stack:
[ 5235.515711] ffff880050053d38 ffffffff8100213d ffff880050053eb0 ffff880050053d10
[ 5235.515842] 0000000000000246 000000000000002e ffffffff811de97d ffff8801a9003c00
[ 5235.515949] ffffffff81183c84 0000000000000018 000000000da6966e ffffffffc05fe000
1
2
3
4
回溯信息

[ 5235.516085] Call Trace:
[ 5235.516127] [<ffffffff8100213d>] ? do_one_initcall+0xcd/0x1f0
[ 5235.516185] [<ffffffff811de97d>] ? kmem_cache_alloc_trace+0x1ad/0x220
[ 5235.516222] [<ffffffff81183c84>] ? do_init_module+0x27/0x1d2
[ 5235.516283] [<ffffffff81183cbd>] do_init_module+0x60/0x1d2
[ 5235.516346] [<ffffffff81104104>] load_module+0x1424/0x1b10
[ 5235.516433] [<ffffffff811008f0>] ? __symbol_put+0x40/0x40
[ 5235.516521] [<ffffffff81206a61>] ? kernel_read+0x41/0x60
[ 5235.516610] [<ffffffff811049be>] SYSC_finit_module+0x7e/0xa0
[ 5235.516706] [<ffffffff811049fe>] SyS_finit_module+0xe/0x10
[ 5235.516802] [<ffffffff81806eb6>] entry_SYSCALL_64_fastpath+0x16/0x75
1
2
3
4
5
6
7
8
9
10
11
以上是堆栈调用跟踪回溯信息, 在Oops发生之前调用的函数的列表.

然后是在 Oops发生时正在运行的机器代码部分的十六进制转储.

cpp[ 5235.516883] Code: <c7> 04 25 00 00 00 00 01 00 00 00 48 89 e5 5d c3 00 00 00 00 00 00
1
1.5 发现问题所在
其中最关键的信息, 就是PC/IP等寄存器的信息, 直接显示了正在执行的代码

[ 5235.513604] IP: [<ffffffffc0008003>] hello_init+0x3/0x1000 [kerneloops]

[ 5235.517020] RIP [<ffffffffc0008003>] hello_init+0x3/0x1000 [kerneloops]
1
2
3
不同的系统中提示的可能有所不同, 不同架构对 PC/IP 寄存器的叫法不同

PC is at sello_init+0x3/0x1000

或者

EIP : hello_init+0x3/0x1000 [kerneloops]

告诉我们内核是执行到 hello_init+0x3/0x1000 这个地址处出错的, 那么我们所需要做的就是找到这个地址对应的代码

格式为 +偏移/长度

hello_init指示了实在hello_init中出现的异常

0x3表示出错的偏移位置

0x1000表示hello_init函数的大小

1.5.1 通过gdb调试列出地址所对应的位置
由于我们的是驱动出现的问题, 那么我们就用gdb直接调试驱动的 KO 文件, 如果是源内核出现的 OOPS, 那么只能用 gdb 对 vmlinux 文件进行调试

# gdb调试驱动
gdb kerneloops.ko

# l/list address列出对应的代码位置
l *(hello_init+0x3)

# 或者 b address在地址出插入断点, 也会提示断点的位置
b *(hello_init+0x3)
1
2
3
4
5
6
7
8

可以看到 gdb 提示 hello_init+0x3 对应的代码是驱动远大第 12 行

*p = 1;
1
由于 p 值一个 NULL 指针, 直接赋值, 导致 NULL 指针异常

此方法对于内核OOPS同样适用, 调试时将驱动 KO 文件替换为内核 vmlinux 文件

1.5.2 addr2line将地址转换为对应的源代码
addr2line -e kerneloops.o hello_init+0x3
1
此方法对于内核OOPS同样适用, 调试时将驱动 KO 文件或者 OBJ 文件替换为内核 vmlinux 文件

1.5.3 将gdb反汇编代码得到地址直接转换为对应的源代码
对于驱动来说, 可以从/sys/module/对应驱动名称/sections/.init.text 查找到对应的地址信息

# 调试驱动代码
gdb kerneloops.ko

# 接下来, 使用 `add-symbol-file` 将符号文件添加到调试器.
add-symbol-file kerneloops.o 0xffffffffa03e1000
# 将hello_init函数反汇编得到虚拟地址信息
disassemble hello_init
#list address+offset的信息
l *(address+offset)
1
2
3
4
5
6
7
8
9

add-symbol-file 命令

第一个参数是驱动的 obj 文件 kerneloops.o

第二个参数是模块的文本部分的地址, 从/sys/module/XXX/sections/.init.text(其中 XXX 是模块名称)获取此地址

首先获取到地址信息

cat /sys/module/kerneloops/sections/.init.text
OR
nm kerneloops.ko | grep hello_init
OR
nm kerneloops.o | grep hello_init
1
2
3
4
5

地址信息是0x0000000000000000

gdb调试驱动kerneloops.ko, 并添加调试信息

gdb kerneloops.ko
add-symbol-file kerneloops.o 0x0000000000000000
1
2

接着将hello_init函数反汇编

disassemble hello_init
1

可以得到hello_init的起始地址为 0x0000000000000024,
那么hello_init+0x03的地址为0x0000000000000027
对应的代码mov DWORD PTR ds:0x0,0x1
可以看到是个0异常

进一步的我们查阅其代码

l *(0x0000000000000027)
1

同样可以得到最后异常的代码在地12行

此方法对于内核OOPS同样适用, 调试时将驱动 KO 文件或者 OBJ 文件替换为内核 vmlinux 文件, 通过 nm vmlinux和 cat /proc/kallsyms 获取到对应的地址信息

1.5.4 使用objdump反汇编代码得到地址
objdump -D *.o得到反汇编代码

objdump -S *.o得到含有c源码的汇编
1
2
3
这要求之前的编译包含了 debug信息 (-g), 而我们的Makefile中添加了 -g 调试选项

objdump -S kerneloops.ko
OR
objdump -S kerneloops.o
1
2
3

可以很明显的看到hello_init偏移0x3出的汇编和对应的代码

*p = 1;
3: c7 04 25 00 00 00 00 movl $0x1,0x0
1
2
直接对地址 0x0 处写入 0x1

此方法对于内核OOPS同样适用, 调试时将驱动 KO 文件或者 OBJ 文件替换为内核 vmlinux 文件

2 参考资料
根据内核Oops 定位代码工具使用— addr2line 、gdb、objdump

转载_Linux内核OOPS调试

kernel panic/kernel oops分析

DebuggingKernelOops

kerneloops package in Ubuntu

Understanding a Kernel Oops!

Kernel oops错误

Kernel Oops Howto

Kernel Panics

WiKipedia

Oops中的error code解释

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可
————————————————
版权声明:本文为CSDN博主「JeanCheng」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/gatieme/article/details/73715860

Linux Kernel PANIC(三)--Soft Panic/Oops调试及实例分析【转】的更多相关文章

  1. 【转】android 电容屏(三):驱动调试之驱动程序分析篇

    关键词:android  电容屏 tp 工作队列 中断 坐点计算  电容屏主要参数平台信息:内核:linux2.6/linux3.0系统:android/android4.0  平台:S5PV310( ...

  2. linux驱动由浅入深系列:高通sensor架构实例分析之二(驱动代码结构)【转】

    本文转载自:https://blog.csdn.net/radianceblau/article/details/73498303 本系列导航: linux驱动由浅入深系列:高通sensor架构实例分 ...

  3. linux驱动由浅入深系列:高通sensor架构实例分析之三(adsp上报数据详解、校准流程详解)【转】

    本文转载自:https://blog.csdn.net/radianceblau/article/details/76180915 本系列导航: linux驱动由浅入深系列:高通sensor架构实例分 ...

  4. Linux Kernel系列三:Kernel编译和链接中的linker script语法详解

    先要讲讲这个问题是怎么来的.(咱们在分析一个技术的时候,先要考虑它是想解决什么问题,或者学习新知识的时候,要清楚这个知识的目的是什么). 我在编译内核的时候,发现arch/arm/kernel目录下有 ...

  5. Linux Kernel 0.12 启动简介,调试记录(Ubuntu1804, Bochs, gdb)

    PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明   本文作为本人csdn blog的主站的备份.(Bl ...

  6. android 电容屏(三):驱动调试之驱动程序分析篇

    平台信息: 内核:linux3.4.39系统:android4.4 平台:S5P4418(cortex a9) 作者:瘋耔(欢迎转载,请注明作者) 欢迎指正错误,共同学习.共同进步!! 关注博主新浪博 ...

  7. Linux Kernel - Debug Guide (Linux内核调试指南 )

    http://blog.csdn.net/blizmax6/article/details/6747601 linux内核调试指南 一些前言 作者前言 知识从哪里来 为什么撰写本文档 为什么需要汇编级 ...

  8. 用Qemu模拟vexpress-a9 (一) --- 搭建Linux kernel调试环境【转】

    转自:http://www.cnblogs.com/pengdonglin137/p/5023342.html#_label2 阅读目录(Content) 环境介绍: 下载Linux内核 安装arm的 ...

  9. 用Qemu模拟vexpress-a9 (一) --- 搭建Linux kernel调试环境

    参考: http://blog.csdn.net/linyt/article/details/42504975 环境介绍: Win7 64 + Vmware 11 + ubuntu14.04 32 u ...

随机推荐

  1. 解决N个人过桥时间最短问题(Java版本)

    [问题描述] n个人要晚上过桥,在任何时候最多两个人一组过桥,每组要有一只手电筒.在这n个人中只有一个手电筒能用,因此要安排以某种往返的方式来返还手电筒,使更多的人可以过桥.   注意:每个人的过桥速 ...

  2. 7. Go语言—时间模块

    一.时间模块 1. 统计程序执行时间 package main import ( "time" "fmt" ) func test() { time.Sleep ...

  3. 字符设备驱动程序之poll机制(韦大仙)

    明确为什么要引用poll机制? while(1) { read(fd,&key_val,1);//如果没有按键按下,它会一直在等待.现在想做这么一件事情:如果5s后,没有按键按下,它就会返回. ...

  4. C++中各种输入函数的用法总结

    1.scanf()函数 原型:int scanf(const char *restrict format,......); 入口参数:第一个参数是格式字符串,它指定了输入的格式,......格式化后的 ...

  5. SecureCRT自动断开连接的解决方法

    方法一: 在普通用户下,输入重启sshd服务的命令:service sshd restart 这时会提示:管理系统服务或单元需要身份验证.解决的方法:先要切换为root用户,接着重启sshd服务:se ...

  6. 2019.6.11_MySQL进阶一:索引

    所谓索引就是为特定的mysql字段进行一些特定的算法排序,比如二叉树的算法和哈希算法,哈希算法是通过建立特征值,然后根据特征值来快速查找.MySQL索引的建立对于MySQL的高效运行是很重要的,索引可 ...

  7. opencv 截取任意四边形区域的图像

    截取任意四边形区域的图像. mask就是结果. 需要定义四边形区域,分别是 tl, tr, bl, br std::map<int, std::set<int>> genera ...

  8. 《Dapper》

    最近看了google的分布式追踪系统dapper的论文:http://static.googleusercontent.com/external_content/untrusted_dlcp/rese ...

  9. 【2019.8.14 慈溪模拟赛 T1】我不是!我没有!别瞎说啊!(notme)(BFS+DP)

    \(IDA^*\) 说实话,这道题我一开始没想出正解,于是写了一个\(IDA^*\)... 但神奇的是,这个\(IDA^*\)居然连字符串长度分别为\(2500,4000\)的数据都跑得飞快,不过数据 ...

  10. A1101 Quick Sort (25 分)

    一.技术总结 这里的一个关键就是理解调换位置排序是时,如果是元主,那么它要确保的条件就只有两个一个是,自己的位置不变,还有就是前面的元素不能有比自己大的. 二.参考代码 #include<ios ...