Description
This is an exercise for kernel coding.
Focus on,
- EXPORT_SYMBOL //communicate between modules
- work_queue
- wait_queue
- spinlock
- list
There are 3 modules, chess.ko, chessplayer0.ko, chessplayer1.ko
References:
http://www.linuxjournal.com/article/6916?page=0,1
http://www.ibm.com/developerworks/linux/library/l-tasklets/index.html
Source files,
chess.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/uaccess.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include "chess.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tico.Feng");
static char chessboard[3][3] = {{' ',' ',' '},{' ',' ',' '},{' ',' ',' '}};
static struct player_list players;
spinlock_t step_lock=SPIN_LOCK_UNLOCKED;
static DECLARE_WAIT_QUEUE_HEAD(chess_wq0);
static DECLARE_WAIT_QUEUE_HEAD(chess_wq1);
static DECLARE_WAIT_QUEUE_HEAD(game_wq);
static bool ready=false; //false, not ready, true, ready
//wait_queue_head_t chess_wq0;
//wait_queue_head_t chess_wq1;
EXPORT_SYMBOL(chess_wq0);
EXPORT_SYMBOL(chess_wq1);
EXPORT_SYMBOL(game_wq);
void showchessboard();
bool gameready();
void registerplayer(struct player_list *player) {
struct player_list *tmp;
tmp = (struct player_list *)kmalloc(sizeof(struct player_list),GFP_KERNEL);
tmp->id=player->id;
tmp->status=player->status;
tmp->go=player->go;
tmp->piece=player->piece;
if(tmp->go == 0) //offensive, bind chess_wq0 for it.
{
tmp->wtq = &chess_wq0;
}else{ // the second partener, bind chess_wq1 for it.
tmp->wtq = &chess_wq1;
}
list_add(&(tmp->list),&(players.list));
if(gameready())
wake_up_interruptible(&game_wq);
}
void cleanplayer()
{
struct player_list *tmp;
struct list_head *pos, *q;
list_for_each_safe(pos,q,&players.list){
tmp = list_entry(pos,struct player_list,list);
list_del(pos);
kfree(tmp);
}
}
EXPORT_SYMBOL(registerplayer);
bool gameready(){
struct player_list *tmp;
int num=0;
list_for_each_entry(tmp,&players.list,list) {
num++;
}
if(num == 2)
ready = true;
else
ready = false;
return ready;
}
EXPORT_SYMBOL(gameready);
int runonestep(struct player_list *player) {
int status=0;
struct player_list *tmp;
if( player->x >= MAXBOARDX || player->y >= MAXBOARDY ) {
printk(KERN_INFO"the step out of board.\n");
return -1;
}
spin_lock(&step_lock);
chessboard[player->x][player->y]=player->piece;
list_for_each_entry(tmp,&players.list,list) {
if(tmp->id == player->id){
printk(KERN_INFO"player %d finish one step.\n",tmp->id);
tmp->status=0; //finish one step, waiting for...
}else{
printk(KERN_INFO"wake up player %d to go.\n",tmp->id);
tmp->status=1; //it turn for opponent to go
wake_up_interruptible(tmp->wtq);
}
}
spin_unlock(&step_lock);
showchessboard();
return status;
}
EXPORT_SYMBOL(runonestep);
int getplayerstatus(struct player_list *player){
int status=0;
struct player_list *tmp;
list_for_each_entry(tmp,&players.list,list) {
if(tmp->id == player->id){
status = tmp->status;
break;
}
}
return status;
}
EXPORT_SYMBOL(getplayerstatus);
void showchessboard() {
printk(KERN_INFO" %c | %c | %c\n",chessboard[0][0],chessboard[0][1],chessboard[0][2]);
printk(KERN_INFO" -------------\n");
printk(KERN_INFO" %c | %c | %c\n",chessboard[1][0],chessboard[1][1],chessboard[1][2]);
printk(KERN_INFO" -------------\n");
printk(KERN_INFO" %c | %c | %c\n\n\n",chessboard[2][0],chessboard[2][1],chessboard[2][2]);
}
static int __init init_chess(void)
{
printk(KERN_INFO "init_chess-->\n");
//struct player_list *tmp;
INIT_LIST_HEAD(&players.list);
//init_waitqueue_head(&chess_wq0);
//init_waitqueue_head(&chess_wq1);
/*
tmp = (struct player_list *)kmalloc(sizeof(struct player_list),GFP_KERNEL);
tmp->id=0;
tmp->status=1;
tmp->go=0;
registerplayer(tmp);
list_for_each_entry(tmp,&players.list,list) {
printk(KERN_INFO "id=%d\n",tmp->id);
printk(KERN_INFO "status=%d\n",tmp->status);
printk(KERN_INFO "go=%d\n",tmp->go);
}
showchessboard();
*/
return 0;
}
module_init(init_chess);
static void __exit exit_chess(void)
{
printk(KERN_INFO "exit_chess-->\n");
cleanplayer();
}
module_exit(exit_chess);
chessplayer0.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/uaccess.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include "chess.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tico.Feng");
extern void registerplayer(struct player_list *player);
extern int runonestep(struct player_list *player);
extern int getplayerstatus(struct player_list *player);
extern bool gameready();
extern wait_queue_head_t game_wq;
extern wait_queue_head_t chess_wq0;
static int tactics[5][2] = { {0,0},{0,2},
{1,1},
{2,0},{2,2} };
static struct player_list player;
static struct workqueue_struct *wq;
void play(work struct *work) {
int steps=5;
int i=0;
if(!gameready()) {
printk(KERN_INFO"game is not ready, waiting for other to join...\n");
wait_event_interruptible(game_wq, gameready() );
printk(KERN_INFO"Start the game...\n");
}
for(i=0;i<steps;i++) {
/* if offensive, start the first step*/
if(player.go == 0 && i==0) {
player.x=tactics[i][0];
player.y=tactics[i][1];
runonestep(&player);
}else{
printk(KERN_INFO "player %d going to sleep status=%d.\n",player.id,getplayerstatus(&player));
wait_event_interruptible(chess_wq0, getplayerstatus(&player) == 1);
printk(KERN_INFO "awken player %d going to play one step.\n",player.id);
player.x=tactics[i][0];
player.y=tactics[i][1];
runonestep(&player);
}
}
work_struct *my_work=work;
kfree((void *)my_work);
}
static int __init init_chess_player(void)
{
struct work_struct *chesswork;
/*init player and register it*/
player.id=0x00;
player.status=0; /*ready to go*/
player.go=0; /*offensize*/
player.piece='0';
registerplayer(&player);
/*
player.x=0;
player.y=0;
runonestep(&player);
*/
wq = create_singlethread_workqueue("my_chess_queue");
chesswork = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
/*init the work struct*/
INIT_WORK(chesswork,play);
queue_work(wq, chesswork);
return 0;
}
module_init(init_chess_player);
static void __exit exit_chess_player(void)
{
flush_workqueue(wq);
destroy_workqueue(wq);
printk(KERN_INFO "exit_chess-->\n");
}
module_exit(exit_chess_player);
chessplayer1.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/uaccess.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include "chess.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tico.Feng");
extern void registerplayer(struct player_list *player);
extern int runonestep(struct player_list *player);
extern int getplayerstatus(struct player_list *player);
extern wait_queue_head_t chess_wq1;
static int tactics[4][2] = { {0,1},{1,0},{1,2},{2,1}};
static struct player_list player;
static struct workqueue_struct *wq;
void play(work_struct *work) {
int steps=4;
int i=0;
for(i=0;i<steps;i++) {
/* if offensive, start the first step*/
if(player.go == 0 && i==0) {
player.x=tactics[i][0];
player.y=tactics[i][1];
runonestep(&player);
}else{
printk(KERN_INFO "player %d going to sleep.\n",player.id);
wait_event_interruptible(chess_wq1, getplayerstatus(&player) == 1);
printk(KERN_INFO "awken player %d going to play one step.\n",player.id);
player.x=tactics[i][0];
player.y=tactics[i][1];
runonestep(&player);
}
}
work_struct *my_work=work;
kfree((void *)my_work);
}
static int __init init_chess_player(void)
{
struct work_struct *chesswork;
/*init player and register it*/
player.id=0x01;
player.status=0; /*wait*/
player.go=1; /*deffensize*/
player.piece='1';
registerplayer(&player);
/*
player.x=0;
player.y=0;
runonestep(&player);
*/
wq = create_singlethread_workqueue("my_chess_queue");
chesswork = kmalloc(sizeof(struct work_struct), GFP_KERNEL);
/*init the work struct*/
INIT_WORK(chesswork,play);
queue_work(wq, chesswork);
return 0;
}
module_init(init_chess_player);
static void __exit exit_chess_player(void)
{
flush_workqueue(wq);
destroy_workqueue(wq);
printk(KERN_INFO "exit_chess-->\n");
}
module_exit(exit_chess_player);
Because module dependency, the source file should be at same directory.
Makefile,
obj-m += chess.o
obj-m += chessplayer0.o
obj-m += chessplayer1.o
KDIR := /usr/src/linux-headers-2.6.32-41-generic
PWD := $(shell pwd)
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
How to play?
$sudo insmod chess.ko //have to insmod it firstly, because chessplayer0.ko and chessplayer1.ko depend on it.
$sudo insmod chessplayer0.ko
$sudo insmod chessplayer1.ko
$tail -f /var/log/syslog // watch game