mtd and /dev/mtd*相關資料

2024-05-28 06:58
文章标签 資料 dev 相關 mtd

本文主要是介绍mtd and /dev/mtd*相關資料,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

轉載自 http://my.oschina.net/shelllife/blog/123482

如有侵權,將刪除

今天做升级方案用到了mtd-utils中的flash_eraseall和flash_cp两个工具,在进行方案验证的时候,遭遇到各种不解和疑惑,因对MTD的原理不熟悉,所以只能多次尝试,虽然最后把方案搞定了,不过觉得MTD中的mtd和mtdblock区别这块还是值得总结学习一下。这里先说明一下问题现象,然后在进行具体的区别原理解释。

MTD设备(Nor Flash)使用中的问题现象表现

    1. mtd-utils工具对mtd和mtdblock分区设备的区别处理
1
2
3
4
5
6
7
/ $ flash_eraseall /dev/mtdblock/2
flash_eraseall: /dev/mtdblock/2 : unable to get MTD device info
/ $ flash_eraseall /dev/mtdblock/2
flash_eraseall: /dev/mtdblock/2 : unable to get MTD device info
/ $ flash_eraseall /dev/mtd/2
Erasing 128 Kibyte @ 8e0000 -- 98 % complete.
/ $ ls
1
2
3
4
5
6
/ $ flashcp rootfs_version /dev/mtdblock2
This doesn't seem to be a valid MTD flash device!
/ $ flashcp rootfs_version /dev/mtdblock/2
This doesn't seem to be a valid MTD flash device!
/ $ flashcp rootfs_version /dev/mtd2
/ $ ls
    1. mtd和mtdblock分区设备mount时的区别
1
2
3
4
5
6
/ $ mount  -t jffs2 /dev/mtd/2  qqzm/
mount : Mounting /dev/mtd/2  on qqzm/ failed: Invalid argument
/ $ mount  -t jffs2 /dev/mtd2  qqzm/
mount : Mounting /dev/mtd2  on qqzm/ failed: Invalid argument
/ $ mount  -t jffs2 /dev/mtdblock/2  qqzm/
/ $ ls
    1. mtdblock挂载成功,单擦除后卸载失败
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/ $ flash_eraseall /dev/mtd/2  <span>< /span > Erasing 128 Kibyte @ 8e0000 -- 98 % complete.
/qqzm  $ mount
/dev/root  on / type  jffs2 (rw,noatime)
proc on /proc  type  proc (rw,nodiratime)
sysfs on /sys  type  sysfs (rw)
devfs on /dev  type  devfs (rw)
devpts on /dev/pts  type  devpts (rw)
/dev/mmcblk0p1  on /mnt/sd  type  vfat (rw,nodiratime,fmask=0022,dmask=0022,codepage=cp437,iocharset=iso8859-1)
/dev/mtdblock/2  on /qqzm  type  jffs2 (rw,noatime)
none on /qqzm/www/cgi-bin/tmp  type  ramfs (rw)
/qqzm  $ cd  ..
/ $ umount  /qqzm
umount : Couldn't umount  /qqzm : Inappropriate ioctl for  device
/ $ umount  /dev/mtdblock/2
umount : Couldn't umount  /dev/mtdblock/2 : Inappropriate ioctl for  device
/ $

通过上面的不断尝试和错误反馈,我把方案基本验证通过了,只是对其中的原理不清楚:

  • 为什么mtd和mtdblock明明是同一个设备分区却有不同的操作?
  • mount命令只能挂载块设备吗?
  • 卸载mtdblock设备时,Inappropriate ioctl for device是什么意思?
  • unable to get MTD device info,又是什么意思?

MTD技术的基本原理

MTD(memory technology device内存技术设备)是用于访问memory设备(ROM、flash)的Linux的子系统。MTD的主要目的是为了使新的memory设备的驱动更加简单,为此它在硬件和上层之间提供了一个抽象的接口,并进行了一个层次划分,层次从上到下大致为:设备文件、MTD设备层、MTD原始设备层、硬件驱动层。MTD的所有源代码在/drivers/mtd子目录下。

系统中的MTD设备文件

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
~ $ ls  /dev/mtd * -l
crw-rw----    1 root     root      90,   0 Jan  1 00:00 /dev/mtd0
crw-rw----    1 root     root      90,   1 Jan  1 00:00 /dev/mtd0ro
crw-rw----    1 root     root      90,   2 Jan  1 00:00 /dev/mtd1
crw-rw----    1 root     root      90,   3 Jan  1 00:00 /dev/mtd1ro
crw-rw----    1 root     root      90,   4 Jan  1 00:00 /dev/mtd2
crw-rw----    1 root     root      90,   5 Jan  1 00:00 /dev/mtd2ro
crw-rw----    1 root     root      90,   6 Jan  1 00:00 /dev/mtd3
crw-rw----    1 root     root      90,   7 Jan  1 00:00 /dev/mtd3ro
brw-rw----    1 root     root      31,   0 Jan  1 00:00 /dev/mtdblock0
brw-rw----    1 root     root      31,   1 Jan  1 00:00 /dev/mtdblock1
brw-rw----    1 root     root      31,   2 Jan  1 00:00 /dev/mtdblock2
brw-rw----    1 root     root      31,   3 Jan  1 00:00 /dev/mtdblock3
/dev/mtd :
crw-rw-rw-    1 root     root      90,   0 Jan  1 00:00 0
cr--r--r--    1 root     root      90,   1 Jan  1 00:00 0ro
crw-rw-rw-    1 root     root      90,   2 Jan  1 00:00 1
cr--r--r--    1 root     root      90,   3 Jan  1 00:00 1ro
crw-rw-rw-    1 root     root      90,   4 Jan  1 00:00 2
cr--r--r--    1 root     root      90,   5 Jan  1 00:00 2ro
crw-rw-rw-    1 root     root      90,   6 Jan  1 00:00 3
cr--r--r--    1 root     root      90,   7 Jan  1 00:00 3ro
/dev/mtdblock :
brw-------    1 root     root      31,   0 Jan  1 00:00 0
brw-------    1 root     root      31,   1 Jan  1 00:00 1
brw-------    1 root     root      31,   2 Jan  1 00:00 2
brw-------    1 root     root      31,   3 Jan  1 00:00 3
~ $

可以看到有mtdN和对应的/dev/mtd/N、mtdblockN和对应的/dev/mtdblock/N两类MTD设备,分别是字符设备,主设备号90和块设备,主设备号31。其中/dev/mtd0和/dev/mtd/0是完全等价的,/dev/mtdblock0和/dev/mtdblock/0是完全等价的,而/dev/mtd0和/dev/mtdblock0则是同一个MTD分区的两种不同应用描述,操作上是有区别的。

/dev/mtdN设备

/dev/mtdN 是MTD架构中实现的mtd分区所对应的字符设备(将mtd设备分成多个区,每个区就为一个字符设备),其里面添加了一些ioctl,支持很多命令,如MEMGETINFO,MEMERASE等。

mtd-utils中的flash_eraseall等工具,就是以这些ioctl为基础而实现的工具,实现一些关于Flash的操作。比如,mtd 工具中 flash_eraseall中:

1
2
3
4
5
if  (ioctl(fd, MEMGETINFO, &meminfo) != 0)
{
    fprintf (stderr, "%s: %s: unable to get MTD device info\n" ,exe_name, mtd_device);
    return  1;
}

MEMGETINFO是Linux MTD中的drivers/mtd/mtdchar.c中的ioctl命令,使用mtd字符设备需要加载mtdchar内核模块。该代码解释了上面的第一个现象。

/dev/mtdblockN设备

/dev/mtdblockN,是Flash驱动中用add_mtd_partitions()添加MTD设备分区,而生成的对应的块设备。MTD块设备驱动程序可以让flash器件伪装成块设备,实际上它通过把整块的erase block放到ram里面进行访问,然后再更新到flash,用户可以在这个块设备上创建通常的文件系统。

而对于MTD块设备,MTD设备层是不提供ioctl的实现方法的,也就不会有对应的MEMGETINFO命令之类,因此不能使用nandwrite,flash_eraseall,flash_erase等工具去对/dev/mtdblockN去进行操作,否则就会出现上面的现象一,同时也解释了现象3——用mtd2擦除分区后,在用mtdblock2进行umount就会造成混乱。

mtd块设备的大小可以通过proc文件系统进行查看:

1
2
3
4
5
6
7
8
9
10
~ $ cat  /proc/partitions
major minor  #blocks  name
   31     0        512 mtdblock0
   31     1       1024 mtdblock1
   31     2       5632 mtdblock2
   31     3       9216 mtdblock3
  254     0   30760960 mmcblk0   
  254     1   30756864 mmcblk0p1
~ $

后面的两个是SD块设备的分区大小。每个block的大小是1KB。

MTD设备分区和总结

通过proc文件系统查看mtd设备的分区情况:

1
2
3
4
5
6
7
~ $ cat  /proc/mtd
dev:    size   erasesize  name
mtd0: 00080000 00020000 "boot"
mtd1: 00100000 00020000 "kernel"
mtd2: 00580000 00020000 "roofs70"
mtd3: 00900000 00020000 "app"
~ $

可以发现,实际上mtdN和mtdblockN描述的是同一个MTD分区,对应同一个硬件分区,两者的大小是一样的,只不过是MTD设备层提供给上层的视图不一样,给上层提供了字符和块设备两种操作视图——为了上层使用的便利和需要,比如mount命令的需求,你只能挂载块设备(有文件系统),而不能对字符设备进行挂载,否则会出现上面的现象2:无效参数。

这里对于mtd和mtdblock设备的使用场景进行简单总结:

  1. mtd-utils工具只能应用与/dev/mtdN的MTD字符设备
  2. mount、umount命令只对/dev/mtdblockN的MTD块设备有效
  3. /dev/mtdN和/dev/mtdblockN是同一个MTD设备的同一个分区(N一样)

================================================================== 


轉載自(英文)

http://www.opensourceforu.com/2012/01/working-with-mtd-devices/


MTD (Memory Technology Devices) are NAND/NOR-based flash memory chips used for storing non-volatile data like boot images and configurations. Readers are cautioned not to get confused with USB sticks, SD cards, etc., which are also called flash devices, but are not MTD devices. The latter are generally found on development boards, used to store boot loaders, an OS, etc.

Even though MTD devices are for data storage, they differ from hard disks and RAM in several aspects. The biggest difference is that while hard disk sectors are rewritable, MTD device sectors must be erased before rewriting — which is why they are more commonly called erase-blocks. Second, hard disk sectors can be rewritten several times without wearing out the hardware, but MTD device sectors have a limited life and are not usable after about 10^3-10^5 erase operations. The worn out erase-blocks are called bad blocks and the software must take care not to use such blocks.

Like hard disks, MTD devices can be partitioned and can therefore act as independent devices. On a system with one or more MTD devices, device and partition information can be obtained from the /proc/mtd file. A typical /proc/mtd file is as follows:

cat /proc/mtd
dev:  size    erasesize name
mtd0: 000a0000 00020000 "misc"
mtd1: 00420000 00020000 "recovery"
mtd2: 002c0000 00020000 "boot"
mtd3: 0fa00000 00020000 "system"
mtd4: 02800000 00020000 "cache"
mtd5: 0af20000 00020000 "userdata"

A partitioned MTD device can be depicted as in Figure 1, which shows the relation between an MTD device, a partition and a sector.

An MTD device

Figure 1: An MTD device

As already said, MTD write operations are different from usual storage devices. Therefore, before we move further, let’s understand how write operations take place on MTD devices. Figure 2 shows a typical write case.

An MTD write operation

Figure 2: An MTD write operation

The left-most part shows a sector that has some data at the end. The rest of the sector has not been written since the last erase. A user wants to write “new data 1″ to this sector at offset 0. Since this part of the sector has already been erased, it is ready to be written and so “new data 1″ can be directly written to the sector. Later, the user may want to write “new data 2″, again at offset 0. To do this, the sector must be erased. Since the sector needs to be erased in entirety, the “old data” must be backed up in a temporary buffer. After erasing the complete sector, the “new data 2″ and “old data” must be written at appropriate offsets.

This procedure is the reason there are specific file systems for MTD devices, like JFFS2 and YAFFFS, and flash translation layers (FTL) like NFTL, INFTL, etc. These FTLs and file systems take special care of MTD device properties to hide complexity from the user.

In the first section that follows, we will look at how to access, read/write and erase MTD devices from Linux applications. The second section describes the same things in kernel space, so that this article can be useful to both application as well as kernel developers.

Accessing MTDs from applications

The user must know the device partition to work upon, which can be found from /proc/mtd as shown earlier. Assuming users want to work on the “userdata” partition, they must use the/dev/mtd5 device.

The first thing to do is to get information about the MTD device. Use the MEMGETINFO ioctlcommand, as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <mtd/mtd-user.h>
int main()
{
     mtd_info_t mtd_info;
     int fd = open( "/dev/mtd5" , O_RDWR);
ioctl(fd, MEMGETINFO, &mtd_info);
     printf ( "MTD type: %u\n" , mtd_info.type);
     printf ( "MTD total size : %u bytes\n" , mtd_info.size);
     printf ( "MTD erase size : %u bytes\n" , mtd_info.erasesize);
     return 0;
}

Error handling has been omitted for brevity. The mtd_info_t structure is used with the MEMGETINFOcommand. The MTD type can be MTD_ABSENTMTD_RAMMTD_ROMMTD_NANDMTD_NOR, etc., which are defined in the mtd/mtd-abi.h header file. The mtd_info.size indicates the size of the whole device (i.e., the partition, in this case). Finally, mtd_info.erasesize indicates the sector size. During an erase operation, this is the minimum size that can be erased, as we’ll see later.

Reading MTD devices is similar to ordinary devices:

/* read something from last sector */
unsigned char buf[64];
lseek(fd, -mtd_info.erasesize, SEEK_END);
read(fd, buf, sizeof (buf));

A write operation can be performed in the same way, provided the sector has been erased previously. Finally, we come to the erase operation. Here is an example of erasing a partition, sector by sector:

void erase_partition(mtd_info_t *mtd_info, int fd) {
     erase_info_t ei;
     ei.length = mtd_info->erasesize;
  
     for (ei.start = 0; ei.start < mtd_info->size; ei.start += mtd_info->erasesize) {
         ioctl(fd, MEMUNLOCK, &ei);
         ioctl(fd, MEMERASE, &ei);
     }
}

All sectors of the device are writeable after this erase operation. Notice the use of MEMUNLOCK beforeMEMERASE, which is essential to allow the erase operation.

Accessing MTDs from kernel space

This section will repeat the functions explained in the previous section, but in kernel space. This needs a separate section since the erase operation is more complex here  –  the erase operation may sleep and therefore the kernel programmer has to wait until the operation is completed. This is the case for applications too, but the sleep is transparently taken care of by the scheduler.

As explained earlier, the first MTD information is the mtd_info structure. This is retrieved by iterating through all registered MTD devices:

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
#include <linux/kernel.h>
#include <linux/mtd/mtd.h>
#include <linux/err.h>
static struct mtd_info *mtd_info = NULL;
  
int init_module( void ) {
     int num;
     for (num = 0; num < 64; num++) {
         mtd_info = get_mtd_device(NULL, num);
         if (IS_ERR(mtd_info)) {
             printk( "No device for num %d\n" , num);
             continue ;
         }
         if (mtd_info->type == MTD_ABSENT) {
             put_mtd_device(mtd_info);
             continue ;
         }
         if ( strcmp (mtd_info->name, "userdata" )) {
             put_mtd_device(mtd_info);
             continue ;
         }
         printk( "MTD type: %u\n" , mtd_info->type);
         printk( "MTD total size : %u bytes\n" , mtd_info->size);
         printk( "MTD erase size : %u bytes\n" , mtd_info->erasesize);
         return 0;
     }
     mtd_info = NULL;
     return 0;
}
  
void cleanup_module( void )
{
if (mtd_info)
         put_mtd_device(mtd_info);
}

The above kernel module searches for the “userdata” partition. The function get_mtd_device(), when invoked with the first argument NULL, returns the MTD device associated with the minor number specified in the second argument. On a successful search, it increments the reference count of the device. That’s why, before exiting, a call to put_mtd_device() must be made to release (decrement) the reference count.

Additionally, the module uses the flag MTD_ABSENT (which is available to applications too). This check is required to function correctly with some probing device drivers used to allocate placeholder MTD devices on systems that have socketed or removable media.

Having retrieved the mtd_info structure, reading is relatively simple:

/* read something from last sector */
u_char buf[64];
mtd_info->read(mtd_info, mtd_info.size-mtd_info.erasesize, sizeof (buf), buf);

The second argument of the read function specifies the read offset, and the third the length to read. Note that the read operation too may sleep and, therefore, it must not be performed in an interrupt context. The write operation can be performed as follows (assuming the sector has been previously erased):

/* write something to last sector */
mtd_info->write(mtd_info, mtd_info.size-mtd_info.erasesize, sizeof (buf), buf);

As mentioned before, the read, write and erase operations may sleep. Therefore, kernel code must wait for the operation to finish. Here is an example of erasing the partition and waiting to finish the operation:

#include <linux/sched.h>
void erase_partition( struct mtd_info *mtd_info) {
     unsigned int start;
     for (start = 0; start < mtd_info->size; start += mtd_info->erasesize)
         erase_sector(mtd_info, start, mtd_info->erasesize);
}
  
void erase_sector( struct mtd_info *mtd_info, unsigned int start, unsigned int len)
{
     int ret;
     struct erase_info ei = {0};
     wait_queue_head_t waitq;
     DECLARE_WAITQUEUE(wait, current);
     
     init_waitqueue_head(&waitq);
     ei.addr = start;
     ei.len = mtd_info->erasesize;
     ei.mtd = mtd_info;
     ei.callback = erase_callback;
     ei.priv = (unsigned long )&waitq;
     ret = mtd_info->erase(mtd_info, &ei);
     if (!ret)     {
         set_current_state(TASK_UNINTERRUPTIBLE);
         add_wait_queue(&waitq, &wait);
         if (ei.state != MTD_ERASE_DONE && ei.state != MTD_ERASE_FAILED)
             schedule();
         remove_wait_queue(&waitq, &wait);
         set_current_state(TASK_RUNNING);
  
         ret = (ei.state == MTD_ERASE_FAILED)?-EIO:0;
     }
}
  
void erase_callback ( struct erase_info *instr) {
     wake_up((wait_queue_head_t *)instr->priv);
}

The erase_partition() function iterates over all sectors, and erases them with erase_sector(). At the core of erase_sector() is the mtd_info->erase call, which (as mentioned previously) may sleep. Therefore, erase_sector() prepares a wait queue and a wait queue head.

After a call to mtd_info->erase, the function prepares itself to relinquish the CPU (presuming thatmtd_info->erase will sleep) by changing task state to TASK_UNINTERRUPTIBLE and adding itself to the wait queue head. Before relinquishing the CPU, it checks if erase is done, through the ei.stateflag. If erase is done successfully, this flag will be set to MTD_ERASE_DONE.

If the erase operation is not complete, the task relinquishes the CPU by calling schedule(). Later, when the erase operation is complete, the driver calls the callback function provided inei.callback. Here the task wakes up to itself, then removes itself from the wait queue, changes the task state to TASK_RUNNING and finally, the erase_sector() function returns.

MTD devices have many more features that can be used by application programmers. ECC (error correction codes) and OOB (out of band) data are some of them. The MTD framework is integrated into the Linux kernel — therefore it makes working with MTD devices very simple, as we have seen in this article.


这篇关于mtd and /dev/mtd*相關資料的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1009852

相关文章

v0.dev快速开发

探索v0.dev:次世代开发者之利器 今之技艺日新月异,开发者之工具亦随之进步不辍。v0.dev者,新兴之开发者利器也,迅速引起众多开发者之瞩目。本文将引汝探究v0.dev之基本功能与优势,助汝速速上手,提升开发之效率。 何谓v0.dev? v0.dev者,现代化之开发者工具也,旨在简化并加速软件开发之过程。其集多种功能于一体,助开发者高效编写、测试及部署代码。无论汝为前端开发者、后端开发者

LVM 'Can’t open /dev/sdb1 exclusively. Mounted filesystem?' Problem

在将几块盘做LVM时,遇到一个之前都没遇到过的问题: root@ubuntu:~# pvcreate /dev/sdc1Can't open /dev/sdc1 exclusively. Mounted filesystem? 首先第一反应就是查看这个分区是否已经在使用了,但是没有。 查看硬盘的一些信息: root@ubuntu:~# cat /proc/partitionsmajo

Linux 挂载须知: mount 一些小问题 mount: no medium found on /dev/sr0

我属于window+VM+ubuntu的配置,有时需要把windows上的文件,挂载到虚拟机上去。然后每次使用命令的时候会出现如下的问题:                其实解决问题很简单,但是由于我自己的粗心而花费了很长的时间:    这个时候一定要确认,虚拟机的设置处是否勾选了   具体如下图所示:                 然后按照  如下的命令:   mount

Ubuntu上安装libdc1394-22-dev出现无法定位安装包的解决办法

一、libdc1394-22-dev介绍       libdc1394-22-dev 是一个开发库,用于与IEEE 1394 (FireWire)摄像头进行交互。具体来说,它是 libdc1394 的开发版本,提供了开发者头文件和链接库,方便在应用程序中集成对基于 IEEE 1394 标准的数码相机的支持。 主要功能: - IEEE 1394 (FireWire) 协议:这是一个支持高速数据

App使用Job中遇到的WAIT DEV_NOT_DOZING的解决方案

摘要: 由于原生Job机制会使用Doze的白名单,故遇到WAIT:DEV_NOT_DOZING现象,一般配置为Whitelist user apps名单即可解决问题 Doze名单类型 配置对象 配置方法 影响 Whitelist user apps 第三方应用 1.Adb shell 命令:adb shell dumpsys deviceidle whitelist +com.t

Linux MTD设备文件系统

1. 文件系统简介 文件系统是一种存储和组织计算机数据的方法,它使得对其访问和查找变得容易,文件系统使用文件和树形目录的抽象逻辑概念代替了硬盘和光盘等物理设备使用数据块的概念,用户使用文件系统来保存数据不必关心数据实际保存在硬盘(或者光盘)的地址为多少的数据块上,只需要记住这个文件的所属目录和文件名。在写入新数据之前,用户不必关心硬盘上的那个块地址没有被使用,硬盘上的存储空间管理(分配和释放)功能

Dev C++:简单步骤下载与安装指南

1. 前言 在当今这个数字化时代,编程已成为连接创意与技术的桥梁,它不仅推动着科技的进步,也深刻地改变着我们的生活方式。对于初学者而言,选择一款合适且易于上手的集成开发环境(IDE)是学习编程旅程中的重要一步。而Dev C++,作为一款轻量级且功能强大的C/C++编程工具,凭借其简洁的界面、丰富的功能以及良好的兼容性,成为了众多编程爱好者尤其是C/C++初学者的首选。 本指南旨在为广大编程爱好

/dev/null笔记

/dev/null      是个黑洞,进去的东西永远出不来,它是一个空设备文件    在脚本编程的时候经常会用它(空设备文件)来屏蔽标准或错误输出(stdout 或 stderr)。 >      重定向输出,覆盖文件 >>     重定向输出,不覆盖文件 &      等同于 UNIX有几种输入输出流,与数字的对应关系如下:      0-标准输入流(stdin)

一种动态防御策略——移动目标防御(MTD)

文章速览: 1、高级规避攻击 2、用移动目标防御对抗欺骗 常见做法操作系统和应用程序才是真正的战场打破游戏规则 网络攻击的技术变得愈发难测,网络攻击者用多态性、混淆、加密和自我修改乔装他们的恶意软件,以此逃避防御性的检测,于是移动目标防御(MTD)技术出现了,通过动态地改变攻击面,有效地对抗日益复杂和隐蔽的网络攻击。 一、高级规避攻击 高级规避攻击可以反复修改网络攻击,包括其源、静态签名

git合并dev分支的多个commit到master分支

这里主要使用新建指定位置分支,rebase,以及删除新建分支,三步操作进行 1.新建到合并的commit处的branch git checkout -b newbranch 74b73d6a 后面跟得的是commit_id,这个commit是最后需要合并到的那个[ , commit_id] 2.rebase到master git checkout mastergit rebase ne