目錄
- 前言
- 使用者态代碼
- 驅動子產品代碼
- per-CPU變量
- 關閉搶占
- 示範
- 最後
之前 說到了基礎的驅動子產品寫法. 這次目标就是計算進入驅動ioctl或者其他某個驅動函數的次數. 當然, 你可能會覺得, 這弄個全局變量計數不就完了嗎? 但是這裡的要求是要并行進行通路, 是以統計的是多核多線程的通路次數. 是不是感覺沒有那麼簡單了? 你可能會回答, 上鎖, 那基本等于串行, 太low, 這裡展示真正的并行通路與計數.
先來看下使用者态代碼:
#include <iostream>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <string>
using namespace std;
#define NUM_THREADS 20
// 檔案描述符
int fd = 0;
// 多線程調用函數
void *pthread_fx( void *args ) {
ioctl( fd, 1, 0 );
}
int main() {
int ret = 0;
// 打開檔案
if((fd = open("/dev/hellodr", O_RDWR)) < 0) {
cerr << strerror(errno) << endl;
return -1;
}
// 開啟多線程
pthread_t tids[NUM_THREADS];
for ( int i = 0; i < NUM_THREADS; ++i ) {
ret = pthread_create( &tids[i], NULL, pthread_fx, NULL );
if ( ret != 0 ) {
printf( "pthread_create error: error_code = %d\n", ret );
}
}
// 回收線程資源
for ( int i = 0; i < NUM_THREADS; ++i ) {
pthread_join(tids[i], NULL);
}
// 關閉檔案
close( fd );
return 0;
}
這裡就是開20個線程調用驅動的ioctl函數. 沒什麼要說的.
驅動代碼
是依據上一次的内容 擴充的, 變化基本在DriverClose和DriverIOControl兩個函數中.
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#define MAJOR_NUM 231
#define DEVICE_NAME "hellodr"
DEFINE_PER_CPU( long, gUsage ) = 0;
int DriverOpen( struct inode *pslINode, struct file *pslFileStruct )
{
printk( KERN_ALERT DEVICE_NAME " hello open.\n" );
return(0);
}
ssize_t DriverWrite( struct file *pslFileStruct, const char __user *pBuffer, size_t nCount, loff_t *pOffset )
{
printk( KERN_ALERT DEVICE_NAME " hello write.\n" );
return(0);
}
int DriverClose( struct inode *pslINode, struct file *pslFileStruct )
{
int i = 0;
unsigned long ulBaseAddr = 0;
unsigned long ulOffset = 0;
long *pUsage = NULL;
long usageSum = 0;
ulOffset = (unsigned long) (&gUsage);
for_each_online_cpu( i )
{
ulBaseAddr = __per_cpu_offset[i];
pUsage = (long *) (ulBaseAddr + ulOffset);
usageSum += (*pUsage);
printk( KERN_ALERT DEVICE_NAME " pUsage = %lx, *pUsage = %ld\n", (unsigned long) pUsage, *pUsage );
}
printk( KERN_ALERT DEVICE_NAME " %ld\n", usageSum );
return(0);
}
long DriverIOControl( struct file *pslFileStruct, unsigned int uiCmd, unsigned long ulArg )
{
long *pUsage = NULL;
/* printk( KERN_ALERT DEVICE_NAME ": pUsage = 0x%lx %lx %ld", (unsigned long) pUsage, (unsigned long) (&gUsage), (*pUsage) ); */
preempt_disable();
pUsage = this_cpu_ptr( (long *) (&gUsage) );
(*pUsage)++;
preempt_enable();
return(0);
}
struct file_operations hello_flops = {
.owner = THIS_MODULE,
.open = DriverOpen,
.write = DriverWrite,
.release = DriverClose,
.unlocked_ioctl = DriverIOControl
};
static int __init hello_init( void )
{
int ret;
ret = register_chrdev( MAJOR_NUM, DEVICE_NAME, &hello_flops );
if ( ret < 0 )
{
printk( KERN_ALERT DEVICE_NAME " can't register major number.\n" );
return(ret);
}
printk( KERN_ALERT DEVICE_NAME " initialized.\n" );
return(0);
}
static void __exit hello_exit( void )
{
printk( KERN_ALERT DEVICE_NAME " removed.\n" );
unregister_chrdev( MAJOR_NUM, DEVICE_NAME );
}
module_init( hello_init );
module_exit( hello_exit );
MODULE_LICENSE( "GPL" );
MODULE_AUTHOR( "Sean Depp" );
但是在說代碼之前, 要說一下per-CPU變量
變量, 也就是DEFINE_PER_CPU( long, gUsage ) = 0;這一行. 可以先看看
這篇文章和
, 簡單來說就是在單cpu環境下生效的變量. 那你可能會說, 我就一個核啊, 豈不是沒用了, 但是這是相對于虛拟cpu來說的, 也就是說, 如果你是4核8線程, 就可以同時有8個這樣的per-CPU變量生效. 正因為有這樣的變量, 計數變得非常簡單, 隻需要統計每個cpu中的這個變量即可. 還有個問題需要解決, 那就是擷取每個cpu的基位址, 這裡又有一個宏for_each_online_cpu(), 隻需要給個變量即可循環輸出活躍的cpu了. 宏真是好東西哦~
多核情況下, 就還有一個問題, 搶占. 當然了, 這裡的代碼簡單, 不關閉搶占大多數情況下也不會出錯, 但是情況複雜的話, 出錯機率就會大大提高, 甚至你不知道怎麼錯的. 而且這可是核心, 錯了往往十分緻命.
preempt_disable();
pUsage = this_cpu_ptr( (long *) (&gUsage) );
(*pUsage)++;
preempt_enable();
這裡還有一個宏this_cpu_ptr, 擷取per cpu變量的線性位址. 這是作業系統的知識了, 我就不多說了, 自行google咯. 那我來說一下, 為什麼要關閉搶占.
試想一下, 擷取到位址之後, 正打算++操作, 結果中斷搶占, 到了另一個核, 之前的位址就對不上了, 這時候進行++操作就完全不對了. 是以為了不發生這樣的問題, 就需要關閉搶占, 當然, 關閉中斷也行, 但是中斷對作業系統影響太大了, 不推薦.
說了這麼多, 萬一示範不出來, 就沒有任何意義, 是以跑下程式. 編譯生成.ko, .out這些不多說了, mknod上篇文章 也說了. 這裡補充一下, 看到了警告, 這是缺了個庫, 最直接的解決方案就是, 安裝這個庫之後, 重編譯核心. 否則其他方案都過于麻煩.
這裡看到有8個cpu, 因為這是4核8線程的cpu, 然後一共跑了20次, 每個核跑的次數也可以看到. 是以, 實驗成功.
喜歡記得點贊, 有意見或者建議評論區見哦~