c-‘scatterlist’在Linux中如何工作?
scatterlist
是Linux内核中的一个数据结构,用于描述分散/聚集I/O(scatter/gather I/O)操作中的数据缓冲区。本攻略将介绍scatterlist
的基概念和使用方法。
scatterlist
的基本概念
在分散/聚集I/O操作中,数据通常存储在多个不续的缓冲区中。例如,当从磁盘读取文件时,文件的数据可能存储在多个磁盘块中。为了避免将这些数据复制到单个连续的缓冲区中,Linux内核使用scatterlist
数据结构来描述这些不连续的缓冲区。
scatterlist
数据结构定义在<linux/scatterlist.h>
头文件中,其定义如下:
struct scatterlist {
unsigned long page_link;
unsigned int offset;
unsigned int length;
dma_addr_t dma_address;
};
其中,page_link
表示指向下一个scatterlist
的指针,offset
表示缓冲区在页面中的偏移量,length
表示缓冲区的长度,dma_address
表示缓冲区的物理地址。
scatterlist
的使用方法
以下是使用scatterlist
进行分散/聚集I/O操作的示例:
#include <linux/scatterlist.h>
void my_scatter_gather_io(void *data, size_t size)
{
struct scatterlist sg[2];
char *buf1 = kmalloc(size/2, GFP_KERNEL);
char *buf2 = kmalloc(size/2, GFP_KERNEL);
unsigned int len1 = size/2;
unsigned int len2 = size - len1;
unsigned int offset = 0;
int i = 0;
// 将数据分散到两个缓冲区中
sg_init_table(sg, 2);
sg_set_buf(&sg[0], buf1, len1);
sg_set_buf(&sg[1], buf2, len2);
// 从缓冲区中聚集数据
for_each_sg(sg, i, 2, offset) {
memcpy(data + offset, sg_virt(&sg[i]), sg[i].length);
offset += sg[i].length;
}
kfree(buf1);
kfree(buf2);
}
在上述示例中,my_scatter_gather_io()
函数用于进行分散/聚集I/O操作。首先,我们将数据分散到两个缓冲中,然后从缓冲区中聚集数据。在分散数据时,我们使用sg_init_table()
函数初始化scatterlist
数组,并使用sg_set_buf()
函数将缓冲区与scatterlist
关联。在聚集数据时,我们使用for_each_sg()
函数遍历scatterlist
数组,并使用memcpy()
函数将数据从缓冲区中复制到目标缓冲区中。
示例1:使用scatterlist
进行DMA操作
以下是使用scatterlist
进行DMA操作的示例:
#include <linux/dma-mapping.h>
#include <linux/scatterlist.h>
void my_dma_transfer(struct device *dev, void *data, size_t size)
{
struct scatterlist sg[2];
dma_addr_t dma_addr1, dma_addr2;
char *buf1 = kmalloc(size/2, GFP_KERNEL);
char *buf2 = kmalloc(size/2, GFP_KERNEL);
unsigned int len1 = size/2;
unsigned int len2 = size - len1;
unsigned int offset = 0;
int i = 0;
// 将数据分散到两个缓冲区中
sg_init_table(sg, 2);
sg_set_buf(&sg[0], buf1, len1);
sg_set_buf(&sg[1], buf2, len2);
// 映射缓冲区到DMA地址
dma_addr1 = dma_map_single(dev, buf1, len1, DMA_TO_DEVICE);
dma_addr2 = dma_map_single(dev, buf2, len2, DMA_TO_DEVICE);
sg_dma_address(&sg[0]) = dma_addr1;
sg_dma_address(&sg[1]) = dma_addr2;
// 执行DMA传输
dma_sync_sg_for_device(dev, sg, 2, DMA_TO_DEVICE);
// ...
// 解除DMA地址映射
dma_unmap_single(dev, dma_addr1, len1, DMA_TO_DEVICE);
dma_unmap_single(dev, dma_addr2, len2, DMA_TO_DEVICE);
kfree(buf1);
kfree(buf2);
}
在上述示例中,my_dma_transfer()
函数用于进行DMA操作。首先,我们将数据分散到两个缓冲中,并使用dma_map_single()
函数将缓冲区映射到DMA地址。然后,我们使用sg_dma_address()
函数将scatterlist
中的DMA地址设置为映射后的地址。在执行DMA传输之前,我们使用dma_sync_sg_for_device()
函数同步scatterlist
中的缓冲区和DMA地址。在DMA传输完成后,我们使用dma_unmap_single()
函数解除DMA地址映射。
示例2:使用scatterlist
进行加密操作
以下是使用scatterlist
进行加密操作的示例:
#include <crypto/scatterwalk.h>
void my_crypto_encrypt(struct crypto_cipher *tfm, void *data, size_t size)
{
struct scatterlist sg[2];
char *buf1 = kmalloc(size/2, GFP_KERNEL);
char *buf2 = kmalloc(size/2, GFP_KERNEL);
unsigned int len1 = size/2;
unsigned int len2 = size - len1;
unsigned int offset = 0;
int i = 0;
// 将数据分散到两个缓冲区中
sg_init_table(sg, 2);
sg_set_buf(&sg[0], buf1, len1);
sg_set_buf(&sg[1], buf2, len2);
// 执行加密操作
scatterwalk_map_and_copy(data, sg, 0, size, 0);
crypto_cipher_encrypt(tfm, sg_virt(&sg[0]), len1);
crypto_cipher_encrypt(tfm, sg_virt(&sg[1]), len2);
scatterwalk_map_and_copy(data, sg, 0, size, 1);
kfree(buf1);
kfree(buf2);
}
在上述示例中,my_crypto_encrypt()
函数用于进行加密操作。首先,我们将数据分散到两个缓冲中,并使用scatterwalk_map_and_copy()
函数将数据从目标缓冲区复制到scatterlist
中的缓冲区中。然后,我们使用crypto_cipher_encrypt()
函数对scatterlist
中的缓冲区进行加密操作。在加密操作完成后,我们使用scatterwalk_map_and_copy()
函数将加密后的数据从scatterlist
中的缓冲区复制到目标缓冲区中。
结论
在本攻略,我们介绍了scatterlist
的基本概念和使用方法。scatterlist
是Linux内核中的一个数据结构,用于描述分散/聚集I/O操作中的数据缓冲区。如果您需要在Linux内核中进行分散/聚集I/O操作、DMA操作或加密操作,可以考虑使用scatterlist
数据结构。