实例代码
#define MAP_SIZE 0x1000
#define MAP_MASK (MAP_SIZE-1)
#define TARGET 0x01C22000
#define VOLUME_VALUE 0x1000003A
void* readRegValue(void* args)
{
int fd;
void* map_base;
void* virt_addr;
unsigned long read_sult;
fd = open("/dev/mem",O_RDWR|O_SYNC);
if(fd == -1)
{
printf("%s:open fd fail\n",__FUNCTION__);
return NULL;
}
printf("%s:open fd %d successfully\n",__FUNCTION__,fd);
printf("%s: %d\n",__FUNCTION__,getpagesize());
//将内核空间映射到用户空间
//第一个参数,所要映射到的虚拟地址,为NULL时,由系统分配
//最后一个参数,所要映射的物理地址,需要进行对齐
map_base = mmap(NULL,MAP_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,fd,TARGET & (~(getpagesize()-1)));
if(map_base == MAP_FAILED)
{
printf("%s:number %d errno:%s\n", __FUNCTION__,errno,strerror(errno));
printf("%s:mmap fail\n",__FUNCTION__);
close(fd);
return NULL;
}
printf("%s:0x%x %p\n",__FUNCTION__,TARGET & ~MAP_MASK,map_base);
virt_addr = map_base + (TARGET & MAP_MASK);
read_sult = *((unsigned long *)virt_addr);
printf("%s:mmap 0x%x\n",__FUNCTION__,read_sult);
printf("%s:0x%x %p 0x%x\n",__FUNCTION__,TARGET & ~MAP_MASK,virt_addr,read_sult);
fflush(stdout);
if(munmap(map_base,MAP_SIZE) == -1)
{
printf("%s:munmap fail\n",__FUNCTION__);
printf("%s:number %d errno:%s\n", __FUNCTION__,errno,strerror(errno));
close(fd);
return NULL;
}
//所需业务处理。
close(fd);
}
所涉及函数说明:
1.open函数
open函数是C语言中用于打开文件的一个函数。其原型如下:
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
其中,pathname表示文件的路径名;flags表示打开方式和操作模式;mode表示文件权限,仅在创建文件时有效。
下面对open函数的参数和返回值进行详细解释。
参数
pathname
pathname是被打开的文件路径名,可以是绝对路径或相对路径,也可以是一个符号链接文件。
flags
flags参数表示打开文件的方式和操作模式,其取值为以下标志位的组合:
标志位 | 含义 |
---|---|
O_RDONLY | 只读方式打开文件。 |
O_WRONLY | 只写方式打开文件。 |
O_RDWR | 读写方式打开文件。 |
O_CREAT | 文件不存在时创建文件。 |
O_TRUNC | 在打开文件时将文件内容清空。 |
O_APPEND | 在文件最后追加数据。 |
O_EXCL | 与O_CREAT一起使用,如果文件已存在不创建,并返回错误信息。 |
O_NOCTTY | 不分配控制终端。 |
O_NONBLOCK | 非阻塞方式打开文件。 |
O_DIRECTORY | 如果pathname不是一个目录,打开文件会失败。 |
O_CLOEXEC | 执行exec()函数时会关闭文件。 |
O_SYNC | 使用同步I/O方式打开文件。 |
mode
mode参数仅在创建文件时有效,用于指定文件的权限(即文件的读写权限),其取值由以下标志位的组合构成:
标志位 | 含义 |
---|---|
S_IRWXU | 文件所有者具有读、写、执行权限。 |
S_IRUSR | 文件所有者具有读权限。 |
S_IWUSR | 文件所有者具有写权限。 |
S_IXUSR | 文件所有者具有执行权限。 |
S_IRWXG | 文件所属组具有读、写、执行权限。 |
S_IRGRP | 文件所属组具有读权限。 |
S_IWGRP | 文件所属组具有写权限。 |
S_IXGRP | 文件所属组具有执行权限。 |
S_IRWXO | 其他用户具有读、写、执行权限。 |
S_IROTH | 其他用户具有读权限。 |
S_IWOTH | 其他用户具有写权限。 |
S_IXOTH | 其他用户具有执行权限。 |
返回值
当open函数调用成功时,返回文件描述符(file descriptor);当open调用失败时,返回-1。文件描述符是对文件的引用,是用于标识打开的文件的整数值。如果打开同一个文件两次,将得到两个不同的文件描述符,但它们引用的仍是同一个文件。
注意事项
-
open函数可以打开普通文件、目录文件、字符设备文件和块设备文件等不同类型的文件。但是,在打开设备文件时,需要root权限。
-
如果使用O_CREAT标志位创建文件,需要使用mode参数指定文件的权限,否则无法访问。
-
本质上,open函数的返回值是一个文件描述符,可以通过read、write等函数对文件进行读写操作,操作完成后需要使用close函数关闭文件。
-
由于open函数是标准库函数,而非系统调用,因此在打开文件时会进行缓冲,所以open函数打开文件的速度较慢,如果需要高效率地打开文件,可以使用系统调用open()函数来代替。
-
在使用open函数时,需要注意文件路径名的正确性,否则会出现打开文件失败的情况。
-
打开文件时需要根据实际需求指定合适的文件访问方式和权限,并且需要注意判断打开文件是否成功。
2.close函数
在C语言中,close函数是用来关闭一个打开的文件描述符的。它的函数原型如下:
#include <unistd.h>
int close(int fd);
其中,fd是需要关闭的文件描述符。
close函数的功能是将文件描述符fd所代表的文件关闭,释放该文件描述符所占用的系统资源。同时,也会将fd指向的文件与该进程的连接断开。
使用close函数后,fd就不能再用于读写文件了。
下面是一个简单的使用close函数的例子:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main()
{
int fd = open("myfile.txt", O_RDONLY);
/* do something with the file */
close(fd);
return 0;
}
在这个例子中,我们打开了一个名为“myfile.txt”的文件,并在操作文件后使用close函数关闭了这个文件。在关闭文件后,我们就无法再使用fd进行文件操作了。
3.getpagesize函数
getpagesize是一个系统调用函数,其作用是获取系统的页面大小。
函数原型:long getpagesize(void);
函数返回值:返回系统页面大小,通常为4KB。
函数说明:
-
页面大小是操作系统内存管理的一个重要概念,指的是操作系统内存分页机制每一块的大小,通常为4KB、8KB或者16KB等。
-
在程序中调用getpagesize函数可以获取当前系统的页面大小,通常可以用于需要手动进行内存管理的程序中。
示例代码:
#include <stdio.h>
#include <unistd.h>
int main() {
long page_size = getpagesize();
printf("Page size is %ld bytes\n", page_size);
return 0;
}
可能的输出为:
Page size is 4096 bytes
4.strerror函数
在C语言中,errno是一个全局变量,用于保存最近一次发生的错误代码。strerror()函数可以将errno的值解释为人类可读的错误消息。
函数原型:char *strerror(int errnum);
参数说明:errnum表示错误码,如果errnum为0,则返回一个空指针。
返回值:返回一个指向错误消息的指针。如果errnum没有对应的错误消息,则返回一个表示未知错误的消息。
示例代码:
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main(){
FILE *file = fopen("nonexistent.txt","r");
if(file == NULL){
printf("Failed to open file: %s\n", strerror(errno));
}
return 0;
}
执行结果:
Failed to open file: No such file or directory
上面的例子演示了如何使用strerror()函数来打印错误消息。在这个例子中,我们试图打开一个不存在的文件,并使用strerror()函数来打印错误消息。strerror()函数解释了errno的值,并将其转换为人类可读的错误消息。
5.fflush函数
fflush函数是C标准库<stdio.h>中的一个函数,其作用是刷新缓冲区。
缓冲区是在程序执行中为了减少I/O(输入输出)调用次数而设计的一种机制。在将数据从程序写入到文件或终端时,数据不会立即被输出,而是先存储到缓冲区中,等到缓冲区满了再一起输出。同样,在从文件或终端中读取数据时,也是先将数据存储到缓冲区中,等到缓冲区满了再一次性读取。
fflush函数可以强制将缓冲区的数据立即输出,而不必等到缓冲区满了。这对于需要实时显示输出结果的程序非常有用。
fflush函数的使用格式为:
int fflush(FILE *stream);
参数stream是一个指向文件流对象的指针,如果stream为NULL,则将所有缓冲区都刷新。
fflush函数的返回值为0表示成功,否则表示失败。
以下是一个简单的fflush函数使用示例:
#include <stdio.h>
int main(){
char buffer[1024];
FILE *fp = fopen("test.txt", "w");
fprintf(fp, "This is testing for fflush...\n");
fflush(fp);
fputs("这里插入文字", fp);
fclose(fp);
return 0;
}
在这个例子中,我们先向文件写入了一行文字并调用了fflush函数强制将缓冲区中的内容写入文件,然后在文件末尾插入了一些文字,并最终关闭了文件。
6.mmap函数
mmap函数是Linux/Unix系统中的一个系统调用函数,用于将文件或设备映射到进程的地址空间中,使得进程可以直接读取和写入该文件或设备的数据。该函数通常用于高速I/O和共享内存等操作。
mmap函数的使用格式如下:
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
其中各参数的含义如下:
- addr:指定映射区域的起始地址,如果为NULL,则表示让系统自动选择。
- length:指定映射区域的长度,以字节为单位。
- prot:指定映射区域的保护方式,可能的值为PROT_READ(可读),PROT_WRITE(可写),PROT_EXEC(可执行)和PROT_NONE(不可访问)。
- flags:指定映射区域的标志,可能的值为MAP_SHARED(共享映射)和MAP_PRIVATE(私有映射)等。
- fd:指定要映射的文件的文件描述符,如果映射的是一个设备,fd通常为-1。
- offset:指定映射区域在文件中的偏移量。
mmap函数的返回值为映射区域的起始地址,如果出现错误,返回MAP_FAILED。
以下是一个简单的mmap函数使用示例:
#include <stdio.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[]) {
int fd;
void *addr;
struct stat sb;
if (argc < 2) {
fprintf(stderr, "Usage: %s <file>\n", argv[0]);
return -1;
}
fd = open(argv[1], O_RDONLY);
if (fd == -1) {
perror("open");
return -1;
}
if (fstat(fd, &sb) == -1) {
perror("fstat");
return -1;
}
addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (addr == MAP_FAILED) {
perror("mmap");
return -1;
}
printf("Mapped file: %s\n", argv[1]);
printf("File size: %lld bytes\n", (long long) sb.st_size);
printf("Mapped address: %p\n", addr);
printf("Contents of the file:\n");
printf("%.*s", (int) sb.st_size, (char *) addr);
munmap(addr, sb.st_size);
close(fd);
return 0;
}
在这个例子中,我们首先打开了一个指定的文件,并获取了该文件的大小,然后调用mmap函数将该文件映射到进程的地址空间中,最后读取了该文件的内容并输出。注意,在使用完mmap函数后,需要调用munmap函数释放映射区域。
总结
mmap函数可以用于将设备寄存器映射到进程的地址空间中,从而可以通过对该映射区域的读写操作来读写设备寄存器。这种方式相比直接使用in/out指令或其他I/O操作函数,具有更高的灵活性和效率。
在使用mmap函数时,需要先打开设备文件,然后调用mmap函数将设备寄存器映射到进程的地址空间中。通常情况下,通过ioctl函数可以获取设备寄存器的地址和长度信息,以便正确地指定映射区域的起始地址和长度。
在读取设备寄存器时,可以将设备寄存器的地址作为指针来访问映射区域中的相应位置,从而达到读取寄存器的目的。在写入设备寄存器时,也可以将相应的值直接写入映射区域中的相应位置。
需要注意的是,在使用mmap函数读写设备寄存器时,需要确保操作的安全性和正确性,尤其是在多线程或多进程环境下,需要进行适当的同步和互斥操作。此外,需要根据不同的体系结构和操作系统进行相应的调整和优化,来达到更好的性能和稳定性。