一、今日学习内容
简单介绍一下函数:
1
|
int (*ioctl) (struct inode * node, struct file *filp, unsigned int cmd, unsigned long arg); |
参数:
- inode和file:ioctl的操作有可能是要修改文件的属性,或者访问硬件。要修改文件属性的话,就要用到这两个结构体了,所以这里传来了它们的指针。
- cmd:命令,接下来会讲
- arg:参数,接下来会讲
返回值: 如果传入的非法命令,ioctl返回错误号-EINVAL。 内核中的驱动函数返回值都有一个默认的方法,只要是正数,内核就会傻乎乎的认为这是正确的返回,并把它传给应用层,如果是负值,内核就会认为它是错误了。
ioctl的cmd cmd就是一个数,如果应用层传来的数值在驱动中有对应的操作,那么就执行,就跟IBinder的transact方法中函数标识是一个道理. 要先定义个命令,就用一个简单的0,来个命令的头文件,驱动和应用函数都要包含这个头文件:
1
2
3
4
5
6
7
|
/*test_cmd.h*/ #ifndef _TEST_CMD_H #define _TEST_CMD_H #define TEST_CLEAR 0/*定义的cmd*/ #endif /*_TEST_CMD_H*/ |
驱动实现ioctl: 命令TEST_CLEAR的操作就是清空驱动中的kbuf。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
int test_ioctl ( struct inode *node, struct file *filp, unsigned int cmd, uns igned long arg) { int ret = 0; struct _test_t *dev = filp->private_data; switch (cmd){ case TEST_CLEAR: memset (dev->kbuf, 0, DEV_SIZE); dev->cur_size = 0; filp->f_pos = 0; ret = 0; break ; default : /*命令错误时的处理*/ P_DEBUG( "error cmd!
" ); ret = - EINVAL; break ; } return ret; } |
然后在应用程序中调用ioctl(fd, TEST_CLEAR);就可以执行驱动程序中的清除kbuf的方法。
ioctl的arg ioctl命令还可以传递参数,应用层的ioctl(fd,cmd,...)后面的“...”是指可以传任意类型的一个参数,注意是一个不是任意多个,只是不检查类型。
binder初始化
我们了解ioctl之后就来看看Binder设备是怎么初始化的,这里介绍的是Binder设备,并不是Binder设备驱动程序,Binder驱动程序是misc设备驱动,要想了解Binder驱动程序的内容,请点击下面链接。
我们的系统服务创建的过程中,要创建打开Binder设备,下面是具体过程。 我们先来介绍下frameworks/native/libs/binder/ProcessState.cpp,ProcessState用来储存当前进程的各种信息,系统服务启动时会创建当前进程的ProcessState单例对象。
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
|
ProcessState::ProcessState() //打开binder : mDriverFD(open_driver()) // //映射内存的起始地址 , mVMStart(MAP_FAILED) , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER) , mThreadCountDecrement(PTHREAD_COND_INITIALIZER) , mExecutingThreadsCount( 0 ) , mMaxThreads(DEFAULT_MAX_BINDER_THREADS) , mStarvationStartTimeMs( 0 ) , mManagesContexts( false ) , mBinderContextCheckFunc(NULL) , mBinderContextUserData(NULL) , mThreadPoolStarted( false ) , mThreadPoolSeq( 1 ) { if (mDriverFD >= 0 ) { //分配虚拟地址空间,完成数据wirte/read,内存的memcpy等操作就相当于write/read(mDriverFD) mVMStart = mmap( 0 , BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0 ); if (mVMStart == MAP_FAILED) { close(mDriverFD); mDriverFD = - 1 ; } } } |
对于一个不懂C++的我,看起来其实挺难受的,但是这段代码很重要,还是要看懂的。 其实我们只需要关注这几行重要代码 open_driver() 下面会讲 mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0) 分配虚拟内存映射 我们先来看open_driver函数
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
|
static int open_driver() { int fd = open( "/dev/binder" , O_RDWR | O_CLOEXEC); //打开 /dev/binder if (fd >= 0) { int vers = 0; //通过ioctl通知binder驱动binder版本 status_t result = ioctl(fd, BINDER_VERSION, &vers); if (result == -1) { ALOGE( "Binder ioctl to obtain version failed: %s" , strerror ( errno )); close(fd); fd = -1; } if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) { ALOGE( "Binder driver protocol does not match user space protocol!" ); close(fd); fd = -1; } //设置当前fd最多支持DEFAULT_MAX_BINDER_THREADS线程数量 size_t maxThreads = DEFAULT_MAX_BINDER_THREADS; result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads); if (result == -1) { ALOGE( "Binder ioctl to set max threads failed: %s" , strerror ( errno )); } } else { ALOGW( "Opening '/dev/binder' failed: %s
" , strerror ( errno )); } return fd; } |
首先执行int fd = open("/dev/binder", O_RDWR | O_CLOEXEC); 获取到了驱动文件的文件描述符。 文件打开成功之后,使用ioctl查询了版本号,并设置了最大的连接线程数。 然后调用mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0)把/dev/binder文件映射到了进程虚拟内存空间,这里我们还要了解下linux的mmap函数。
mmap参考自:blog.itpub.net/7728585/vie…
在LINUX中我们可以使用mmap用来在进程虚拟地址空间中分配创建一片虚拟内存地址映射
我们可以在当前进程的虚拟内存中获得一块映射区域,我们直接操作映射区域就可以间接操作内核中的文件。 我们使用mmap的目的是创建共享文件映射
进程都有一份文件映射,并且都指向同一个文件,这样就实现了共享内存,Binder就是利用这种共享内存方式去进行数据的交互。每个进程都会保留一份dev/binder设备的映射区域,这样我们利用Binder,数据经过一次拷贝就可以实现跨进程,Linux的管道机制则需要四次拷贝
二、遇到的问题
暂无
三、明日计划
继续Android学习