partprobe工具
作業系統目錄
/usr/sbin/partprobe
程式安裝包
parted-3.1-17.el7.x86_64.rpm
指令用法:
partprobe
是用來告知作業系統核心 分區表發生變化的工具,告知方式是請求核心重讀分區表
選項如下:
-d
不會讓核心重讀分區表,分區表發生變化後使用該指令
partproe -d /dev/sdi
不會告知核心分區發生了變化
-s
先讓核心重讀分區表,再顯示裝置已擁有的分區資訊
[root@node1 ~]# partprobe -s /dev/hda
/dev/hda: msdos partitions 1 2 3 4 <5 6 7 8 9> #4分區為擴充分區,符号<内部的分區為邏輯分區
-h
顯示幫助選項資訊
-v
顯示程式的版本資訊
源碼分析
- 正常的
函數的指令行解析getopt_long
- 根據是否傳入磁盤進行判斷,如果傳入磁盤,如
partprobe /dev/sdb
則進行磁盤處理;如未傳入制定磁盤,則先擷取到目前裝置所有的磁盤,并加入到連結清單中,然後順序執行分區表處理
中的主函數處理如下partprobe.c
int n_dev = argc - optind; //擷取是否傳入磁盤參數
if (n_dev != 0) {//如果傳入則一個一個處理
int i;
for (i = optind; i < argc; i++) {
PedDevice *dev = ped_device_get (argv[i]);
if (dev == NULL || process_dev (dev) == 0) //process_dev分區處理的主函數
status = 1;
}
} else {//否則先擷取目前裝置所有磁盤
ped_device_probe_all ();//将擷取到的磁盤加入到全局連結清單中
PedDevice *dev;
for (dev = ped_device_get_next (NULL); dev;//周遊連結清單,一個一個順序執行分區檢測
dev = ped_device_get_next (dev))
if (process_dev (dev) == 0)
status = 1;
}
- 從磁盤将分區表讀到記憶體中,并告知作業系統核心分區表的布局
static int process_dev (PedDevice* dev)
{
PedDiskType* disk_type;
PedDisk* disk;
disk_type = ped_disk_probe (dev); //擷取分區類型,gpt?msdos?
if (!disk_type || !strcmp (disk_type->name, "loop"))
return 1;
/*建立一個新的分區表,并存放在記憶體中。當調用ped_disk_commit_to_dev函數,由核心決定是否寫入磁盤*/
disk = ped_disk_new (dev);
if (!disk)
goto error;
/*該參數為我們的-d參數,即partprobe -d /dev/sdb 表示不會執行告知核心磁盤分區情況的操作,如傳入-d參數,則目前數值為1*/
if (!opt_no_inform) {
if (!ped_disk_commit_to_os (disk)) //核心操作的主函數
goto error_destroy_disk;
}
/*該參數表示 partprobe -s參數,執行是否列印磁盤分區資訊的操作*/
if (opt_summary)
/*列印目前磁盤分區的情況*/
summary (disk);
ped_disk_destroy (disk);
return 1;
error_destroy_disk:
ped_disk_destroy (disk);
error:
return 0;
}
- 核心操作分區表版本如下,
函數操作ped_disk_commit_to_os
- 在核心版本為2.4之前,partprobe會調用BLKRRPART ioctl函數讓核心重讀分區表
- 在2.4.x之後的核心已經更改為使用新的blkpg接口告訴核心每一個磁盤分區的起始,結束位址。是以,在新版本核心中,不會因為分區表類型不同而無法讀取磁盤分區表
//通過ped_disk_commit_to_os 函數調用的回掉函數disk_commit(disk)進行核心分區的處理
ped_disk_commit_to_os(PedDisk* disk){
...
if (!ped_architecture->disk_ops->disk_commit (disk))
}
PedDiskArchOps linux_disk_ops = {
partition_get_path: linux_partition_get_path,
partition_is_busy: linux_partition_is_busy,
disk_commit: linux_disk_commit //通過改資料結構調用linux_disk_commit函數進行處理
};
/*告知核心磁盤分區的布局*/
static int linux_disk_commit (PedDisk* disk)
{
if (!_has_partitions (disk))
return 1;
/*若系統支援邏輯卷方式管理磁盤,且磁盤分區類型為支援dm,一般為msdos格式即mbr分區,則重讀分區表。一半存放在/dev/mapper目錄*/
#ifdef ENABLE_DEVICE_MAPPER
if (disk->dev->type == PED_DEVICE_DM)
return _dm_reread_part_table (disk);
#endif
/*如果裝置類型為普通檔案類型,即檔案形式存放的,一般為我們的gpt分區類型的磁盤*/
if (disk->dev->type != PED_DEVICE_FILE) {
/*要求目前核心支援blkpg的新特性,即使用blkpg接口告知核心磁盤分區表的起始位址,不需要核心自己去讀。如果
目前核心不支援該特性,或者這裡發現有斷言退出,可以直接将改行注釋掉*/
assert (_have_blkpg ());
/*核心同步磁盤分區表的主函數*/
if (!_disk_sync_part_table (disk))
return 0;
}
return 1;
}
- 核心同步磁盤分區表的操作,即我們partprobe的主要操作
- 通過io裝置驅動程式的通道管理函數ioctl,先從核心移除所有的分區表。但是當ioctl執行失敗時,不會對此時失敗的分區表進行移除
- 從我們周遊到的磁盤中添加分區表。如果我們因為第一步移除分區表的失敗無法添加(因為使用的是blkpg新特性,移除以及添加需要保證分區表的起始結束位址一直才能确定執行成功),則進行告警
static int _disk_sync_part_table (PedDisk* disk)
{
...
/*從/sys/block/dev_name/range檔案中擷取目前系統支援的單個磁盤最大分區數量,如果擷取不到,則使用預設的64*/
lpn = _device_get_partition_range(disk->dev);
...
/*對目前磁盤執行分區删除操作
通過調用_blkpg_part_command(disk->dev, &linux_part,BLKPG_DEL_PARTITION)
函數執行ioctl (arch_specific->fd, BLKPG, &ioctl_arg)的分區删除操作
*/
_blkpg_remove_partition (disk, j + 1);
...
/*同樣通過
_blkpg_part_command (disk->dev, &linux_part,
BLKPG_ADD_PARTITION)
函數執行ioctl (arch_specific->fd, BLKPG, &ioctl_arg)的分區添加操作
*/
_blkpg_add_partition (disk, part);
/*根據執行的結果是否抛出異常進行告警*/
}
-
"Error informing the kernel about modifications to "
"partition %s – %s. This means Linux won’t know "
"about any changes you made to %s until you reboot "
"-- so you shouldn’t mount it or use it in any way "
"before rebooting.
該問題為核心向磁盤添加分區過程中ioctl程式執行失敗,一般為目前分區表x相關中繼資料資訊錯誤
-
Partition(s) %s on %s have been written, but we have "
"been unable to inform the kernel of the change, "
"probably because it/they are in use. As a result, "
"the old partition(s) will remain in use. You "
"should reboot now before making further changes.
該問題為核心删除分區表時出現,因為分區仍然被占用,無法從緩存中清除該分區的起始和結束位址導緻。