3.2 Android专用驱动和组件
Android专用驱动和组件并非Linux中标准的内容,而是纯软件的内容,与体系结构和硬件平台无关。由于Android专用驱动是纯软件的内容,因此在平台移植的过程中基本不需要做更改,最多是进行配置、选择专用驱动和组件是否使用。
↘3.2.1 电源管理部分
wakelock(醒的状态锁)机制与earlysuspend(早期挂起)结合,提供了Android独有的电源管理机制。相比Android系统其他的内核驱动和组件,内存管理是全局的功能,对具体驱动程序的实现部分有所影响。
它们的头文件是include/linux/目录中的wakelock.h和earlysuspend.h,实现的内容在kernel/power/目录,包括wakelock.c、userwakelock.c、earlysuspend.c等几个源文件。
wakelock可以阻止系统进入挂起,也就是一直醒的状态,而earlysuspend是一个全局的功能,用于让各个模块注册早期挂起的操作。
例如,在Linux系统的命令行中进行如下操作:
#echo "mem" > /sys/power/state
向state写入"mem"等命令本身是Linux电源管理的标准操作,含义为挂起到内存,在普通的Linux系统中,将直接迫使内核进入挂起(suspend)状态。但是在Android中,将调用各个内核模块early_suspend中的suspend函数指针,调用完成后,系统进入“早期挂起”状态。此后,如果系统没有任何wakelock锁,将继续调用各个模块的挂起功能,进入真正的挂起状态;如果有wakelock锁,系统将保持“早期挂起”状态,此时的CPU依然是可以运行的。
↘3.2.2 staging中的组件和驱动程序
Linux内核的drivers/staging/android/目录中放置了Android系统阶段性的内核组件及驱动程序。这个目录中所有的内容都是Android的Linux内核特有的。
目录中包含了Kconfig和Makefile文件,Makefile的内容如下所示:
obj-$(CONFIG_ANDROID_BINDER_IPC) += binder.o obj-$(CONFIG_ANDROID_LOGGER) += logger.o obj-$(CONFIG_ANDROID_RAM_CONSOLE) += ram_console.o obj-$(CONFIG_ANDROID_TIMED_OUTPUT) += timed_output.o obj-$(CONFIG_ANDROID_TIMED_GPIO) += timed_gpio.o obj-$(CONFIG_ANDROID_LOW_MEMORY_KILLER) += lowmemorykiller.o
其中,binder和logger是两个普通的misc驱动程序,timed_output是Android特有的驱动程序框架;timed_gpio是基于timed_output的一个驱动程序;lowmemorykiller是一个内存管理的组件;ram_console是一个利用控制台驱动的框架。
·binder .c和logger.c:基于MISC的字符设备驱动程序。
·lowmemorykiller.c和logger.c:一个内存管理的组件。
·timed_output.c:一个设备驱动程序的框架,使用sys文件系统。
·timed_gpio.c:基于timed_output框架的一种驱动程序。
·ram_console.c:用于调试的内核控制台,使用proc文件系统。
1.Binder驱动程序
Binder驱动程序为用户层程序提供了IPC(进程间通信)支持,Android整个系统的运行依赖Binder驱动。Binder提供给用户空间的接口是主设备号为10的Misc字符设备,次设备号是动态生成的。在用户空间中,Binder设备节点为/dev/binder。
Binder驱动程序内容由binder.h和binder.c这两个文件组成。Binder设备对用户空间主要提供mmap、poll、ioctl等接口。
binder.h中的BinderDriverReturnProtocol和BinderDriverCommandProtocol两个枚举类型分别表示返回和命令,在Binder驱动的ioctl中使用,内容如下所示:
enum BinderDriverReturnProtocol { // 表示返回的整数值 BR_ERROR = _IOR('r', 0, int), BR_OK = _IO('r', l), // 其他返回值 } enum BinderDriverCommandProtocol { // 表示命令的整数值 BC_TRANSACTION = _IOW('c', 0, struct binder_transaction_data), BC_REPLY = _IOW('c', l, struct binder_transaction_data), // 其他命令 }
Binder驱动的实现和使用都比较复杂。在用户空间内需要对其调用poll接口阻塞和mmap映射其中内容。
在Android系统的用户空间中,servicemanager可执行程序和Binder库都调用了Binder驱动程序,然后再提供机制给Android系统的其他框架部分使用。
2.Logger驱动程序
Android的Logger驱动程序为用户层程序提供日志支持。Logger提供给用户空间的接口是主设备号为10的Misc字符设备,次设备号是动态生成的(3个)。
在Android系统的用户空间中,Logger在/dev/log/目录中有3个设备节点,如下所示。
·/dev/log/main:主要的log的设备节点。
·/dev/log/event:事件的log的设备节点。
·/dev/log/radio:Modem部分的log的设备节点。
Logger驱动程序为用户空间提供了ioctl、read和异步write等接口。
Logger驱动程序中几个ioctl的命令在logger.h中定义如下所示:
#define __LOGGERIO 0xAE #define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, l) // log的大小 #define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) // log可用的长度 #define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) // 下一个入口的长度 #define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) // 刷新log
Logger驱动程序中并列地实现了几个设备,它们都具有读接口和异步写(aid_write)接口。对于非本用户且非本组的用户,Logger驱动程序的设备节点是只可写不可读的。也就是说,任何程序都可以输出信息,但是特定用户才可以获取信息。
在Android的用户空间,liblog库和logcat可执行程序是对logger设备进行的调用,通过后者看到的日志信息来自logger设备。
3.Lowmemorykiller组件
Lowmemorykiller提供了在低内存状况下杀死进程的功能。使用lowmemorykiller可以在用户空间设置一个内存的阈值,通过这个阈值来判断进程是不是将要被杀死。
Lowmemorykiller的实现只包括lowmemorykiller.c一个源文件。本组件通过调用Linux内存管理系统的接口,注册一个shrinker,这个shrinker就是Lowmemorykiller的实现。主要内容如下所示:
static struct shrinker lowmem_shrinker = { .shrink = lowmem_shrink, .seeks = DEFAULT_SEEKS * l6 }; module_param_named(cost, lowmem_shrinker.seeks, int, S_IRUGO | S_IWUSR); module_param_array_named(adj, lowmem_adj, int, &lowmem_adj_size, S_IRUGO | S_IWUSR); module_param_array_named(minfree, lowmem_minfree, uint, &lowmem_minfree_size, S_IRUGO | S_IWUSR); module_param_named(debug_level, lowmem_debug_level, uint, S_IRUGO | S_IWUSR);
Lowmemorykiller使用了以下4个sys系统文件作为到用户空间的接口,几个文件的含义如下所示。
·/sys/module/lowmemorykiller/parameters/minfree:最小内存的阀值,整数数组。
·/sys/module/lowmemorykiller/parameters/adj:几个级别,整数数组。
·/sys/module/lowmemorykiller/parameters/debug_level:调试级别,整数。
oom_adj是内存管理机制的一种策略,oom_adj(Out of Memory Adjust)的数值越小,代表进程的优先级别越高。例如,当minfree小于数值A时,结束oom_adj大于数值B的进程。
Android用户空间的应用程序被system_server的ActivityManagerService负责,前台应用进程的oom_adj为0,后台为15,本地的守护进程为负值。
4.Timed Output驱动程序框架
Timed Output(定时输出)实际上是一个驱动程序的框架,用于定时发出一个输出。Timed Output框架为注册的每一个驱动提供sys文件系统的接口。
Timed Output驱动程序框架如图3-1所示。
图3-1 Timed Output驱动程序框架
Timed Output将注册/sys/class/timed_output/目录,每一个注册实现的Timed Output设备将在其中建立一个与设备同名的子目录,子目录中具有一个enable文件,对这个文件的读/写用于显示和控制设备。
timed_output.h中定义timed_output_dev结构体,其内容如下所示:
struct timed_output_dev { const char *name; void (*enable)(struct timed_output_dev *sdev, int timeout); // 使能设备并设置 int (*get_time)(struct timed_output_dev *sdev); // 返回剩余时间(milliseconds) struct device *dev; // 以下为私有数据 int index; int state; };
Timed Output提供了一个驱动程序的框架结构,需要各个设备实现enable和get_time这两个接口,框架通过sys文件系统提供用户空间的接口。Timed Output设备的注册和注销函数如下所示:
extern int timed_output_dev_register(struct timed_output_dev *dev); extern void timed_output_dev_unregister(struct timed_output_dev *dev);
在timed_output.c的实现中,直接调用sys文件系统,主要的内容是show和store这两个函数,如下所示:
static ssize_t enable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct timed_output_dev *tdev = dev_get_drvdata(dev); int remaining = tdev->get_time(tdev); return sprintf(buf, "%d\n", remaining); } static ssize_t enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { struct timed_output_dev *tdev = dev_get_drvdata(dev); int value; sscanf(buf, "%d", &value); // 更新内容 tdev->enable(tdev, value); // 使能设备 return size; } static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, enable_show, enable_store);
enable文件是每个Timed Output设备中都具有的文件,写这个文件表示设置定时器时间并启动定时器,读这个文件表示查看定时器剩余的时间。
基于Timed Output驱动程序框架实现的驱动程序,则主要需要实现enable和get_time这两个函数指针,需要驱动程序自己去实现。
5.Timed GPIO驱动程序
Timed GPIO实际上是一个基于Timed Output驱动程序框架的驱动程序,用于定时控制GPIO(General Purpose Input Output,通用输入/输出端口)。它调用Timed Output框架注册了一个驱动程序,在enable和get_time函数中调用gpio子系统的内容实现功能。
Timed GPIO驱动的名称在timed_gpio.h中定义,如下所示:
#define TIMED_GPIO_NAME "timed-gpio"
根据Timed Output的驱动架构/sys/class/timed_output/timed-gpio目录中的enable文件是Timed GPIO对用户空间的接口。
在驱动的实现过程中,struct timed_gpio作为这个驱动程序的私有结构体,保存GPIO的相关数据。在timed_gpio.c中,调用Timed Output的接口,注册了这个驱动程序。
6.Ram控制台
Ram Console提供了一种可以辅助调试的内核机制,其实现方式是利用一块内存构建一个虚拟的Console设备。当内核中打印信息(调用printk)时,调试信息将同时输出到这个设备中。Ram Console提供给用户空间的接口是Proc文件系统中的proc/last_kmsg文件。
ram_console.c的初始化代码在Proc文件系统中增加了一个last_kmsg文件,其文件的操作在ram_console_file_ops中实现。last_kmsg文件支持读取,读取的内容就是Console的输出信息。
Ram Console通过/proc/last_kmsg的文件给用户空间提供信息,其内容表示Linux内核最后打出的信息,每条信息前面的[]中的内容表示信息的时间。
↘3.2.3 几个主要核心模块
1.Ashmem模块
Ashmem(Anonymous Shared Memory)的含义是匿名共享内存,可以为用户空间程序提供分配内存的机制。Ashmem提供给用户空间的接口是主设备号为10的Misc字符设备,次设备号是动态生成的。在用户空间中,Ashmem的设备节点为/dev/ashmem。
Ashmem驱动程序的配置选项为CONFIG_ASHMEM,对应于init/目录中的Kconfig。由于其具有内存管理的性质,Ashmem的源代码是内存管理的mm目录中的ashmem.c,在include/linux/中的ashmem.h文件是Ashmem系统的头文件。
ashmem.c中提供了内存分配的机制,也实现了一个Misc字符设备作为到用户空间的接口,支持mmap和ioctl操作。ashmem_area为其核心结构体,range_alloc、range_del和range_shrink几个函数完成内存的操作。
Ashmem的ioctl的命令在ashmem.h中定义,内容如下所示:
#define ASHMEM_SET_NAME _IOW(__ASHMEMIOC, l, char[ASHMEM_NAME_LEN]) #define ASHMEM_GET_NAME _IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN]) #define ASHMEM_SET_SIZE _IOW(__ASHMEMIOC, 3, size_t) #define ASHMEM_GET_SIZE _IO(__ASHMEMIOC, 4) #define ASHMEM_SET_PROT_MASK _IOW(__ASHMEMIOC, 5, unsigned long) #define ASHMEM_GET_PROT_MASK _IO(__ASHMEMIOC, 6) #define ASHMEM_PIN _IOW(__ASHMEMIOC, 7, struct ashmem_pin) #define ASHMEM_UNPIN _IOW(__ASHMEMIOC, 8, struct ashmem_pin) #define ASHMEM_GET_PIN_STATUS _IO(__ASHMEMIOC, 9) #define ASHMEM_PURGE_ALL_CACHES _IO(__ASHMEMIOC, l0)
Ashmem为Android系统提供了内存分配功能,实现类似malloc的功能,更类似于POSIX的共享内存。
Android用户空间C工具库libcutils对Ashmem进行封装并提供接口,对其他部分提供了匿名共享内存在用户空间的调用封装。Dalvik虚拟机在创建虚拟机时使用Ashmem作为堆内存,内部的内存分配均来自这块内存,方便了垃圾回收。
2.Pmem模块
Pmem主要用于共享大量的连续物理内存,这种内容常常用于系统中额外的DSP处理器等硬件。Pmem提供给用户空间的接口是主设备号为10的Misc字符设备,次设备号是动态生成的。
Pmem模块的配置选项为CONFIG_ANDROID_PMEM,Pmem的头文件是include/linux目录中的android_pmem.h文件,drivers/misc/目录中的pmem.c是其实现。
在某个实际设备的实现中,通过引用android_pmem.h并调用其中的内容,来构建Pmem,如下所示:
struct pmem_region { unsigned long offset; // 区域的偏移量 unsigned long len; // 区域的长度 }; int is_pmem_file(struct file *file); int get_pmem_file(int fd, unsigned long *start, unsigned long *vstart, unsigned long *end, struct file **filp); int get_pmem_user_addr(struct file *file, unsigned long *start, unsigned long *end); void put_pmem_file(struct file* file); void flush_pmem_file(struct file *file, unsigned long start, unsigned long len); int pmem_setup(struct android_pmem_platform_data *pdata, long (*ioctl)(struct file *, unsigned int, unsigned long), int (*release)(struct inode *, struct file *)); int pmem_remap(struct pmem_region *region, struct file *file, unsigned operation);
相关的操作包括建立、得到和修改Pmem文件,得到用户空间的地址。在具体硬件实现的过程中,调用以上的内容,对Pmem文件进行操作。
Pmem提供了mmap和ioctl作为到用户空间的接口,一些ioctl命令如下所示:
#define PMEM_IOCTL_MAGIC 'p' #define PMEM_GET_PHYS _IOW(PMEM_IOCTL_MAGIC, l, unsigned int) #define PMEM_MAP _IOW(PMEM_IOCTL_MAGIC, 2, unsigned int) #define PMEM_GET_SIZE _IOW(PMEM_IOCTL_MAGIC, 3, unsigned int) #define PMEM_UNMAP _IOW(PMEM_IOCTL_MAGIC, 4, unsigned int) #define PMEM_ALLOCATE _IOW(PMEM_IOCTL_MAGIC, 5, unsigned int) #define PMEM_CONNECT _IOW(PMEM_IOCTL_MAGIC, 6, unsigned int) #define PMEM_GET_TOTAL_SIZE _IOW(PMEM_IOCTL_MAGIC, 7, unsigned int)
这些ioctl命令包含了获得物理内存、映射和解除映射内存、获得内存尺寸、分配、连接和获得全部大小等功能。其中的连接操作,可以让两个以上的进程打开同一个Pmem,从而获得共享内存的效果。
3.Alarm驱动程序
Alarm驱动程序为用户空间提供了一个时钟的接口。它和RTC系统密切相关,起到封装的作用,同时使用了Android系统的wake_lock等功能。Alarm提供给用户空间的接口是主设备号为10的Misc字符设备,次设备号是动态生成的。在用户空间中,Alarm设备节点为/dev/alarm。
Alarm驱动程序的配置选项为CONFIG_RTC_INTF_ALARM(较新版本还有另一个选项CONFIG_RTC_INTF_ALARM_DEV),其头文件是include/linux/中的android_alarm.h文件,drivers/rtc/目录中alarm.c和alarm-dev.c(在较新版本中才有)是其实现部分。
Alarm驱动程序利用内核中的RTC部分,但是和具体的RTC驱动程序没有直接关系。Alarm驱动程序本身依然是硬件无关的。
Alarm可以提供一些ioctl的命令供用户空间调用,如下所示:
#define ANDROID_ALARM_CLEAR(type) _IO('a', 0 | ((type) << 4)) #define ANDROID_ALARM_WAIT _IO('a', l) #define ALARM_IOW(c, type, size) _IOW('a', (c) | ((type) << 4), size) #define ANDROID_ALARM_SET(type) ALARM_IOW(2, type, struct timespec) #define ANDROID_ALARM_SET_AND_WAIT(type) ALARM_IOW(3, type, struct timespec) #define ANDROID_ALARM_GET_TIME(type) ALARM_IOW(4, type, struct timespec) #define ANDROID_ALARM_SET_RTC _IOW('a', 5, struct timespec) #define ANDROID_ALARM_BASE_CMD(cmd) (cmd & ~(_IOC(0, 0, 0xf0, 0))) #define ANDROID_ALARM_IOCTL_TO_TYPE(cmd) (_IOC_NR(cmd) >> 4)
以上ioctl命令主要用于设置警报器的时间,设置RTC(实时时钟)的时间,获取当前时间等。
4.ADB Garget驱动程序
ADB Garget驱动程序是一种USB Garget驱动程序。如果选定此Garget驱动,Android设备作为一个USB设备时,提供ADB的接口。
在Linux中,USB Garget的功能是在设备端使用的功能,每一个硬件只能选定一个。这个ADB Garget是其中的一个,它实际包含了ADB调试功能和大容量存储器(Mass Storage)的功能。
ADB Garget驱动程序是在drivers/usb/gadget目录中,其Makefile的相关内容如下所示:
obj-$(CONFIG_USB_ANDROID) += g_android.o g_android-objs := android.o f_adb.o f_mass_storage.o
其中,android.c为实现USB Garget功能主要的文件,f_adb.c是ADB功能的文件,f_mass_storage.c是标准的文件,需要包含它的目的是为了同时实现大容量存储器的功能。
实现的主要结构体为usb_composite_driver,这表示的就是一种USB Garget功能组合的结构,内容如下所示:
static struct usb_composite_driver android_usb_driver = { .name = "android_usb", .dev = &device_desc, .strings = dev_strings, .bind = android_bind, };
android.c中同时注册了一个MISC设备:/dev/android_adb_enable,当打开这个设备时,表示使能ADB Garget的功能。
这里实现的具体内容是根据Android的ADB的协议来完成的。具体的实现在f_adb.c中完成,这个文件实现了一个USB的功能,调用如下函数增加功能:
ret = usb_add_function(c, &dev->function);
f_adb.c中也注册了一个MISC设备:/dev/android_adb,这个设备可以读/写。
在Android系统的用户空间中,/system/core/adb目录中的内容和ADB相关。这里生成了主机使用的ADB工具和目标机器使用的adbd守护进程的可执行程序。
5.Android Paranoid网络
Android Paranoid网络是一个对Linux内核网络部分的改动,通过这个改动增加了网络的认证机制。这个特性通过宏ANDROID_PARANOID_NETWORK进行使能。
这个特性主要影响了Linux源代码中以下一些文件。
·net/ipv4/af_inet.c:IPV4的协议文件。
·net/ipv6/af_inet6.c:IPV6的协议文件。
·net/bluetooth/af_bluetooth.c:蓝牙的协议文件。
·security/commoncap.c:安全性的文件。
事实上,af_inet.c、af_inet6.c和af_bluetooth.c是3种不同的网络协议中处理协议方面的文件,它们在逻辑上是并列的关系。
Android的头文件include/linux/android_aid.h中定义了关于网络的部分AID,这个内容和标准的Linux有所区别,如下所示:
#ifndef _LINUX_ANDROID_AID_H #define _LINUX_ANDROID_AID_H #define AID_NET_BT_ADMIN 300l #define AID_NET_BT 3002 #define AID_INET 3003 #define AID_NET_RAW 3004 #define AID_NET_ADMIN 3005 #endif
在net/ipv4/af_inet.c中,相关的内容如下所示:
#ifdef CONFIG_ANDROID_PARANOID_NETWORK #include <linux/android_aid.h> static inline int current_has_network(void) { return in_egroup_p(AID_INET) || capable(CAP_NET_RAW); } #else static inline int current_has_network(void){ return l;} #endif
在这个地方,进一步检查了AID(Android的用户ID),如果符合才返回1,如果没有附加这个特性,则直接返回1。
在安全性的文件security/commoncap.c中,相关的内容如下所示:
int cap_capable(struct task_struct *tsk, const struct cred *cred, int cap, int audit) { #ifdef CONFIG_ANDROID_PARANOID_NETWORK if (cap == CAP_NET_RAW && in_egroup_p(AID_NET_RAW)) return 0; // 跳过的操作 if (cap == CAP_NET_ADMIN && in_egroup_p(AID_NET_ADMIN)) return 0; #endif return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM; }
这里的工作是检查能力方面,增加了对AID的判断。如果AID符合,则直接返回0,不再使用cap_raised()函数进行处理。
↘3.2.4 辅助的模块和改动
1.keychord模块
keychord模块是一个辅助输入功能驱动程序,当用户自定义的组合按键发生事件后,可以提供通知。keychord提供给用户空间的接口是主设备号为10的Misc字符设备,次设备号是动态生成的。在用户空间中,其设备节点为/dev/keychord。
keychord的配置选项为CONFIG_INPUT_KEYCHORD,头文件为include/linux/目录中的keychord.h,其实现的内容为drivers/input/misc/目录中的keychord.c。
keychord.h文件中具有如下形式的定义:
struct input_keychord { __ul6 version; __ul6 id; __ul6 count; // 按键码的数目 __ul6 keycodes[]; // 按键码的数组 };
keychord的实现是对普通input驱动程序的一层封装。input_keychord结构中包含了按键码的数组,只有当所有的按键都被按下时,才会产生通知事件。
keychord提供给用户空间的主要是read和poll接口,前者用于获得事件,后者用于阻塞,还有write接口用于配置这个驱动程序。
2.keyreset模块
keyreset模块是一个辅助复位功能的驱动程序。keyreset提供给用户空间的接口是主设备号为10的Misc字符设备,次设备号是动态生成的。在用户空间中,其设备节点为/dev/keyreset。
keyreset的配置选项CONFIG_INPUT_KEYRESET,头文件为include/linux/目录中的keyreset.h,其实现的内容为drivers/input/目录中的keyreset.c。
keyreset.h文件具有以下的定义:
struct keyreset_platform_data { int *keys_up; // 抬起事件 int keys_down[]; // 按下事件,使用0表示结束 };
keyreset基于input驱动架构来实现。在不同平台的实现过程中,只需要使用keyreset_platform_data数据结构定义复位按键,当几个按键按下、几个按键抬起时表示复位事件的发生。
3.kernel_debugger模块
kernel_debugger模块提供辅助的内核调试信息功能。kernel_debugger利用输出的方式提供给用户空间内核的调试信息。
kernel_debugger驱动程序的配置选项是CONFIG_KERNEL_DEBUGGER_CORE,头文件为include/linux/目录的kernel_debugger.h,实现为drivers/misc/目录中的kernel_debugger.c。头文件kernel_debugger.h具有如下定义:
struct kdbg_ctxt { int (*printf)(void *cookie, const char *fmt, ...); void *cookie; }; int kernel_debugger(struct kdbg_ctxt *ctxt, char *cmd);
当在各个驱动程序中调用这个kernel_debugger()函数时,获得相应的信息。
4.uid_stat模块
uid_stat模块是一个根据用户ID统计其网络接收和发送内容的工具。uid_stat提供给用户空间的接口是proc文件系统的/proc/uid_stat目录中的各个文件。
uid_stat的配置选项为CONFIG_UID_STAT,头文件为include/linux/目录中的uid_stat.h,其实现的内容为drivers/misc/目录中的uid_stat.c。
uid_stat.h具有如下定义:
extern int update_tcp_snd(uid_t uid, int size); extern int update_tcp_rcv(uid_t uid, int size);
uid_stat将建立proc文件系统的/proc/uid_stat目录,每个uid在其中具有一个子目录,其中又具有tcp_snd和tcp_rcv两个文件,分别表示该uid发送和接收TCP的情况。
如果uid_stat功能使能,内核的网络核心部分的套接字实现net/socket.c中,将调用uid_stat的接口统计网络收发情况。