mmap はファイルまたは他のオブジェクトをメモリにマップするため、アプリケーション層は copy_to_user 関数を使用せずにドライバー層のデータを直接読み取ることができます。
大量のデータの読み取りと書き込みが必要な LCD などの周辺機器に使用できます。データ。
1. アプリケーション層
mmap の使用法:
- open システムコールでファイルを開き、記述子 fd を返します。
- mmap でメモリ マップを作成し、マップの先頭アドレス ポインタの先頭を返します。
- マッピング (ファイル)、表示 (printf)、変更 (strcpy、memncpy、sprintf、直接変更など) に対するさまざまな操作を実行します。
- メモリ マッピングをオフにするには、 munmap (void *start, size_t length)を使用します。
- closeシステムコールでファイル fd を閉じます。
mmap関数:
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
パラメータ | 意味 |
---|---|
アドレス | マップされるメモリの開始アドレスを指します。通常はNULLに設定されます。 これは、システムが自動的にアドレスを選択し、マッピングが成功した後にこのアドレスに戻ることを意味します。 |
長さ | メモリにマップするファイルの量を表します。 |
プロット | マップされた領域を保護する方法。次の方法を組み合わせることができます。 PROT_EXEC マッピング領域は実行可能 PROT_READマッピング領域は読み取り可能 PROT_WRITEマッピング領域は書き込み可能 PROT_NONE マッピング領域はアクセス不可 |
フラグ | マップされたエリアに影響を与えるさまざまなプロパティ。 mmap() を呼び出すときは、MAP_SHAREDまたはMAP_PRIVATEを指定する必要があります。 |
fd | メモリにマップするファイル記述子。 匿名メモリマッピングが使用される場合、flags に MAP_ANONYMOUS が設定され、fd が -1 に設定されます。 一部のシステムは匿名メモリ マッピングをサポートしていません。fopen を使用して /dev/zero ファイルを開き、 ファイルをマップすることで、匿名メモリ マッピングの効果を得ることができます。 |
オフセット | ファイル マッピングのオフセットは通常 0 に設定され、 ファイルの先頭から対応することを意味し、オフセットはページ サイズの整数倍でなければなりません。 |
メモリマッピング後、write と read を使用してメモリの読み書きを行うこともできますが、効率は低くなります mmap 関数によって返されたアドレスを直接操作したり、strcpy などの文字列処理関数を使用したりでき
ます
応用例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#define MMSIZE 128
int main(int argc, char *argv[])
{
int len;
char wr_buf[] = "hello mmap";
char read_buf[1024];
char *start;
...
...
/*open*/
int fd;
fd = open(argv[1], O_RDWR);
if(fd < 0){
printf("open failed\n");
return -2;
}
printf("pid = %d\n", getpid());
/* 将文件映射到进程的虚拟内存空间 */
start = mmap(NULL, MMSIZE, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
printf("mmap address = 0x%x\n", start);
/* 写数据 */
strcpy(start, wr_buf);
/* 读数据 */
strcpy(read_buf, start);
// read(fd, read_buf, MMSIZE);
printf("new data = %s\n", start);
printf("old data = %s\n", read_buf);
while(1){
sleep(5);
}
munmap(start, MMSIZE);
close(fd);
return 0;
}
2 番目にドライバー層
- まず、ドライバーはメモリのセクションを割り当てます。
- 次に、ユーザー プロセスは、ライブラリ関数 mmap() を通じてカーネル空間にマップするメモリ量をカーネルに伝えます。
- 一連の関数呼び出しの後、カーネルは、対応するドライバーの file_operation で指定された xxx_mmap 関数を呼び出します。
- xxx_mmap 関数でremap_pfn_range () を呼び出して、マッピング関係を確立します。
ドライバーの例:
......
......
#define MM_SIZE 1024 * 8
#define MIN(x,y) (x < y ? x : y)
static char *buf = NULL;
static ssize_t hello_read (struct file *filp, char *buff, size_t size, loff_t *offset)
{
int err;
err = copy_to_user(buff, buf, MIN(MM_SIZE, size));
return MIN(MM_SIZE, size);
}
static int hello_mmap (struct file *filp, struct vm_area_struct *vma)
{
/*设置属性: cache,buffer */
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
if(remap_pfn_range(vma,//虚拟内存区域,即设备地址将要映射到这里
vma->vm_start,//虚拟空间的起始地址
virt_to_phys(buf)>>PAGE_SHIFT,//与物理内存对应的页帧号,物理地址右移12位
vma->vm_end - vma->vm_start,//映射区域大小,一般是页大小的整数倍
vma->vm_page_prot))//保护属性,
{
return -EAGAIN;
}
return 0;
}
/*定义 file_operations 结构体*/
static const struct file_operations hello_drv = {
.read = hello_read,
.open = hello_open,
.mmap = hello_mmap,
......
......
};
/*入口函数*/
static int hello_init(void)
{
//内核申请内存只能按页申请,申请该内存以便后面把它当作虚拟设备
buf = kmalloc(MM_SIZE, GFP_KERNEL);
strcpy(buf, "abc"); //先放入一些数据
......
......
return 0;
}
/*退出函数*/
static void hello_exit(void)
{
kfree(buf);
......
......
}
......
......
3. テスト
mmap のパラメータがMAP_SHAREDの場合、 strcpy で読み取られるデータと書き込まれたデータは同じであり、
mmap のパラメータがMAP_PRIVATEの場合、 strcpy で読み取られるデータと書き込まれたデータは同じであり、データの読み取りと書き込み 入力が異なります (読み取りは元のメモリ データであるため、MAP_PRIVATE フラグにより、カーネルはメモリ データを書き込むときに自動的にメモリ空間をコピーします。そのため、読み取りは元のメモリで、書き込まれたデータはコピーされた新しいメモリに書き込まれます)