콘텐츠로 바로가기

now0930 일지

이런저런 생각

  • 홈
  • 비공개
  • 강좌
  • 잔여 작업 조회
  • 위치

workqueue 실습

리눅스가 work queue를 다양하게 사용한다. 나는 초짜라 DECLARE_WORK와 INIT_WORK를 구분할 수 없었다. DECLARE_WORK가 work struct를 전역 변수로 선언한다. 여러 work로 같은 데이터에 접근할 수 있다. workqueue funtion() 파라미터로 work struct를 넣는데, 여기로 work를 전달하면 된다. 코드가 넝마조각이 되고 있다.

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>     //GPIO


#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/fcntl.h>


#include <linux/workqueue.h>

#define GPIO_10_OUT (10)
#define DEVICE_DATA_MAX 256

unsigned int GPIO_irqNumber;

/*\uc0ac\uc6a9\uc790 \ub370\uc774\ud130 \ubd80\ubd84*/
static struct my_device_data{
	struct cdev cdev;
	int index;
	char my_string[DEVICE_DATA_MAX];
	struct work_struct mywork;
	struct work_struct work_read;
} my_data_global;

struct my_device_data *work_ptr;
struct my_device_data *work_ptr_read;
struct my_device_data *my_data;

/*workque initiate*/

void workqueue_fn(struct work_struct *work);

//defien workqueue_fn//

void workqueue_fn(struct work_struct *work_ptr)
{
	/*work_ptr\ub85c \uc0ac\uc6a9\uc790 \uc815\uc758 \ub370\uc774\ud130 \uc811\uadfc*/
	struct my_device_data *my_pointer;
	//container of \ub85c \uc0ac\uc6a9\uc790 \ub370\uc774\ud130 \uc811\uadfc.
	my_pointer = container_of(work_ptr, struct my_device_data, mywork);
	my_pointer->index++;
	pr_info("Executing workqueue function\n");
	pr_info("index is %d\n",my_pointer->index);

}


void workqueue_read(struct work_struct *work_ptr)
{
	/*work_ptr\ub85c \uc0ac\uc6a9\uc790 \uc815\uc758 \ub370\uc774\ud130 \uc811\uadfc*/
	struct my_device_data *my_pointer;
	//container of \ub85c \uc0ac\uc6a9\uc790 \ub370\uc774\ud130 \uc811\uadfc.
	my_pointer = container_of(work_ptr, struct my_device_data, work_read);
	my_pointer->index++;
	pr_info("opened, index is %d\n", my_pointer->index);

}

static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
{
	/*Scheduling work queue*/
	schedule_work(&work_ptr->mywork);
	pr_info("workqueue scheduled \n");
	return IRQ_HANDLED;

}


//device driver \uc791\uc131 \ubd80\ubd84.
/*************\ub4dc\ub77c\uc774\ubc84 \ud568\uc218 ******************/
static int mydriver_open(struct inode *inode, struct file *file);
static int mydriver_release(struct inode *inode, struct file *file);
static ssize_t mydriver_read(struct file *flip,
		char *buf, size_t len, loff_t *off);
static ssize_t mydriver_write(struct file *flip,
		const char *buf, size_t len, loff_t *off);
/********************************************/



//file operation structure

static struct file_operations fops = 
{
	.owner = THIS_MODULE,
	.read = mydriver_read,
	.write = mydriver_write,
	.open = mydriver_open,
	.release = mydriver_release,
};

static int mydriver_open(struct inode *inode, struct file *file)
{
	pr_info("Deviced file was opend.\n");
	//my_data_global.index++;
	
	return 0;

}

static int mydriver_release(struct inode *inode, struct file *file)
{
	pr_info("Deviced file was closed.\n");
	return 0;
}


static int mydriver_read(struct file *file,
		char *buf, size_t len, loff_t *off)
{
	struct my_device_data *my_data;
	size_t datalen, read_len;
	my_data = (struct my_device_data*)&my_data_global;
	datalen = strlen(my_data->my_string);
	//\ucd5c\ub300\uac12\uc73c\ub85c \uac15\uc81c \uc124\uc815
	if(len > datalen)
	{
		len = datalen;
	}
	read_len = len - *off;
	if (read_len <= 0)
		return 0;
	pr_info("start %p, offset is %lld, read_len is %d\n", my_data->my_string, *off, read_len);
	if(copy_to_user(buf, my_data->my_string+*off, read_len))
		return -EFAULT;
	//read_len = datalen-*off;
	*off += read_len;
	pr_info("kernel has %d, read %d characters from kernel\n", datalen, read_len);
	//zero\ub97c \ubc18\ud658\ud560 \ub54c\uae4c\uc9c0 \ubc18\ubcf5.
	schedule_work(&work_ptr->work_read);
	return read_len;
}

static int mydriver_write(struct file *flip,
		const char *buf, size_t len, loff_t *off)
{
	size_t datalen, write_len, start_pos;
	my_data = &my_data_global;
	start_pos = strlen(my_data->my_string);
	datalen = strlen(my_data->my_string);
	if(len > datalen)
	{
		len = DEVICE_DATA_MAX;
	}

	write_len = len - *off;

	if(copy_from_user(my_data->my_string + start_pos + *off, buf, write_len))
		return -EFAULT;
	*off += write_len;
	return write_len;
}

dev_t dev = 0;
static struct cdev my_cdev;
static struct class *dev_class;
static int __init init_hw(void)
{
	//\ub514\ubc14\uc774\uc2a4 \ub4f1\ub85d
	if(( alloc_chrdev_region(&dev, 0, 1, "test_device") < 0))
	{
		pr_err("[!]character device was not allocated\n");
		goto r_unreg;

	}
	pr_info("[=]%d-%d, was allocated\n", MAJOR(dev), MINOR(dev));


	//\ucd08\uae30\ud654
	cdev_init(&my_cdev, &fops);
	pr_info("[=]driver was initialized\n");


	//\uc2dc\uc2a4\ud15c\uc5d0 \ucd94\uac00
	if((cdev_add(&my_cdev, dev, 1)) < 0)
	{
		pr_err("[!]cannot add device to kernel\n");
		goto r_del;

	}


	//class \ub9cc\ub4e6.
	if((dev_class=class_create(THIS_MODULE, "my_class")) == NULL)
	{
		pr_err("[!]cannot add class\n");
		goto r_class;
	}


	if((device_create(dev_class, NULL, dev, NULL, "my_device")) == NULL)
	{

		pr_err("[!]cannot create device\n");
		goto r_device;
	}
	work_ptr = kmalloc(sizeof(*work_ptr), GFP_KERNEL);
	if(work_ptr == NULL)
	{
		pr_err("[!]cannot allocate memory\n");
		goto r_work;
	}
	INIT_WORK(&work_ptr->mywork, workqueue_fn);



	//work_ptr_read = kmalloc(sizeof(*work_ptr), GFP_KERNEL);
	INIT_WORK(&work_ptr->work_read, workqueue_read);

	//gpio 10\ubc88\uc744 \uc0ac\uc6a9.
	//export\ud558\uc5ec \uac04\ub2e8\ud788 \uc0ac\uc6a9.
	//\uc785\ub825\uc740 \uac12\uc744 \uc368 \ub123\uc744 \uc218 \uc5c6\uc74c. \ucd9c\ub825\uc73c\ub85c \uc124\uc815.
	GPIO_irqNumber = gpio_to_irq(GPIO_10_OUT);
	pr_info("[=]irq %d was assinged\n",GPIO_irqNumber);

	//interrupt \ub4f1\ub85d \ud544\uc694
	if (request_irq(GPIO_irqNumber,
				(void*)gpio_irq_handler,
				IRQF_TRIGGER_RISING,
				"my_device",
				NULL))
	{
		pr_err("[!]my_device: cannot register IRQ\n");
		goto r_gpio;
	}
	pr_info("[=]module was installed\n");
	return 0;
r_gpio:
	gpio_free(GPIO_10_OUT);
r_device:
	device_destroy(dev_class,dev);

r_class:
	class_destroy(dev_class);
r_del:
	cdev_del(&my_cdev);

r_unreg:
	unregister_chrdev_region(dev,1);
r_work:
	kfree(work_ptr);

	return -1;
}

static void __exit exit_hw(void) {
	free_irq(GPIO_irqNumber, NULL);
	gpio_free(GPIO_10_OUT);
	//flush_work(struct work_struct *work);
	device_destroy(dev_class,dev);
	//class_unregister(dev_class);
	class_destroy(dev_class);
	cdev_del(&my_cdev);
	unregister_chrdev_region(dev,1);
	flush_work(&work_ptr->mywork);
	kfree(work_ptr);
	printk(KERN_INFO "module was removed\n");
}


module_init(init_hw);
module_exit(exit_hw);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("noname");
MODULE_DESCRIPTION("Hello, world!");

모듈을 로딩하고 인터럽트를 만들면 schedule_work로 등록한 workqueue_fn을 실행한다.

[   52.456926] [=]236-0, was allocated
[   52.456935] [=]driver was initialized
[   52.457621] [=]irq 54 was assinged
[   52.457693] [=]module was installed
[   68.542830] workqueue scheduled 
[   68.542855] Executing workqueue function
[   68.542866] index is 1
[   69.549436] workqueue scheduled 
[   69.549460] Executing workqueue function
[   69.549466] index is 2
[   70.552638] workqueue scheduled 
[   70.552659] Executing workqueue function
[   70.552663] index is 3

파일을 오픈하면 같은 데이터를 다른 work로 index를 접근할 수 있다. schedule_work(&work_ptr->work_read)를 디바이스를 읽을 때마다 실행한다.

[   86.495196] Deviced file was opend.
[   86.495242] Deviced file was closed.
[   91.476276] Deviced file was opend.
[   91.476318] start 9f3ac859, offset is 0, read_len is 5
[   91.476327] kernel has 5, read 5 characters from kernel
[   91.476367] opened, index is 4
[   91.476430] Deviced file was closed.
[  105.664089] Deviced file was opend.
[  105.664138] start 9f3ac859, offset is 0, read_len is 5
[  105.664149] kernel has 5, read 5 characters from kernel
[  105.664197] opened, index is 5
[  105.664275] Deviced file was closed.

https://www.programmersought.com/article/61671813037/

Workqueue

https://stackoverflow.com/questions/7937245/how-to-use-linux-work-queue/7938990

https://github.com/fervagar/kernel_modules/blob/master/workQueue.c

이 글 공유하기:

  • Tweet
발행일 2021-07-19글쓴이 이대원
카테고리 생활코딩 태그 linux, raspberry, workqueue, 임베디드 리눅스

댓글 남기기응답 취소

이 사이트는 Akismet을 사용하여 스팸을 줄입니다. 댓글 데이터가 어떻게 처리되는지 알아보세요.

글 내비게이션

이전 글

character device를 tasklet으로 테스트

다음 글

timer 실습

2025 5월
일 월 화 수 목 금 토
 123
45678910
11121314151617
18192021222324
25262728293031
4월    

최신 글

  • common mode, differential mode 2025-05-11
  • signal conditioner, 신호 처리기 2025-05-10
  • strain gage 2025-05-09
  • 칼만 필터 2025-05-01
  • positioner(I/P) 2025-04-26

카테고리

  • 산업계측제어기술사
  • 삶 자국
    • 책과 영화
    • 투자
  • 생활코딩
    • LEGO
    • ROS
    • tensorflow
  • 전기기사
  • 피아노 악보

메타

  • 로그인
  • 엔트리 피드
  • 댓글 피드
  • WordPress.org

페이지

  • 소개
  • 잔여 작업 조회
    • 작업 추가
    • 작업의 사진 조회
    • 작업 수정 페이지
  • 사진
    • GPS 입력된 사진
    • 사진 조회
  • 위치
    • 하기 휴가 방문지
    • 해외 출장

태그

android bash c docker driver FSM gps java kernel LEGO linux mysql network program opcua open62541 plc programmers python raspberry reinforcementLearning ros state space system program tensorflow transfer function 경제 미국 민수 삼국지 세계사 실기 에너지 역사 유전자 일본 임베디드 리눅스 전기기사 조선 중국 채윤 코딩 테스트 통계 한국사 한국어

팔로우하세요

  • Facebook
now0930 일지
WordPress로 제작.
 

댓글 로드중...