[태그:] mutex

  • spinlock, mutex 실습

    모듈을 로딩하면 kernel thread부터 만들어야 한다. 같은 책 초반에 나왔는데, 다시 보니 기억할 수 없다. 스레드를 만든 후 함수와 데이터를 넘겨야 하는데, 데이터가 void 포인터다. struct로 캐스팅 하고 싶은데, 에러가 났다. kernel 코드를 보고 괄호를 몇 번 붙였다. 아! ㅅㅂ. 구글 찾아보기보다 시(간)성비가 더 좋다.

    pi@raspberrypi:~/linux $ grep -wn "kthread_create" -r ./drivers
    ./drivers/usb/usbip/usbip_common.h:285:		= kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
    ./drivers/usb/atm/ueagle-atm.c:2228:	sc->kthread = kthread_create(uea_kthread, sc, "ueagle-atm");
    ./drivers/usb/atm/usbatm.c:977:	t = kthread_create(usbatm_do_heavy_init, instance, "%s",
    ./drivers/usb/gadget/file_storage.c:3527:	fsg->thread_task = kthread_create(fsg_main_thread, fsg,
    ./drivers/usb/gadget/function/u_serial.c:1059:	info->console_thread = kthread_create(gs_console_thread,
    ./drivers/usb/gadget/function/f_mass_storage.c:2924:			kthread_create(fsg_main_thread, common, "file-storage");
    ./drivers/usb/host/dwc_common_port/dwc_common_fbsd.c:981:	retval = kthread_create((void (*)(void *))func, data, &thread->proc,
    ./drivers/iio/adc/ina2xx-adc.c:840:	task = kthread_create(ina2xx_capture_thread, (void *)indio_dev,
    
    pi@raspberrypi:~/linux $ vi ./drivers/usb/host/dwc_common_port/dwc_common_fbsd.c
    dwc_thread_t *DWC_THREAD_RUN(dwc_thread_function_t func, char *name, void *data)
    {
            int retval;
            dwc_thread_t *thread = DWC_ALLOC(sizeof(*thread));
    
            if (!thread) {
                    return NULL;
            }
    
            thread->abort = 0; 
            retval = kthread_create((void (*)(void *))func, data, &thread->proc,
                                    RFPROC | RFNOWAIT, 0, "%s", name);
            if (retval) {
                    DWC_FREE(thread);
                    return NULL;
            }
    
            return thread;
    }

    spinlock을 만든 후 초기화 하지 않으면, 사용할 수 없다. kernel에 메모리를 할당 받으면 초기화는 꼭 해줘야 하는 느낌이다.

    #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>
    
    
    #define GPIO_10_OUT (10)
    #define DEVICE_DATA_MAX 256
    
    #include <linux/kthread.h>
    #include <linux/delay.h>
    #include <linux/spinlock.h>
    
    
    unsigned int GPIO_irqNumber;
    
    /*사용자 데이터 부분*/
    struct my_device_data{
    	struct cdev cdev;
    	int index;
    	char my_string[DEVICE_DATA_MAX];
    	struct timer_list simple_timer;
    	spinlock_t lock;
    	struct task_struct *kthread1;
    	struct task_struct *kthread2;
    } ;
    
    int run_this_thread1(void* data);
    int run_this_thread2(void* data);
    
    int run_this_thread1(void* data)
    {
    	//while(1)로 하면 kthread_stop을 할 수 없음.
    	//루프가 없으면 kthread_stop을 불렀을 경우, segment error.
    	//thread가 없다면, 종료를 할 수 없어 보임..
    	while(!kthread_should_stop())
    	{
    	//data is my_device_data
    	struct my_device_data *ptr_main_data;
    	//ptr_main_data = data;
    	ptr_main_data = (struct my_device_data(*))data;
    
    	//공통이 구조체 접근.
    	spin_lock(&ptr_main_data->lock);
    	ptr_main_data->index++;
    	//pr_info("spin lock is %0x\n",ptr_main_data->lock);
    	pr_info("==============\n");
    	pr_info("[+]index is %d\n", ptr_main_data->index);
    	pr_info("==============\n");
    	spin_unlock(&ptr_main_data->lock);
    	//pr_info("spin lock is %0x\n",ptr_main_data->lock);
    
    	msleep(500);
    	}
    	return 0;
    
    }
    
    int run_this_thread2(void* data)
    {
    	while(!kthread_should_stop())
    	{
    	//data is my_device_data
    	struct my_device_data *ptr_main_data;
    	ptr_main_data = (struct my_device_data(*))data;
    	//
    	//공통이 구조체 접근.
    	spin_lock(&ptr_main_data->lock);
    	ptr_main_data->index++;
    	pr_info("==============\n");
    	pr_info("[+]index is %d\n", ptr_main_data->index);
    	pr_info("==============\n");
    	spin_unlock(&ptr_main_data->lock);
    	msleep(500);
    	}
    
    	return 0;
    
    }
    
    static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
    {
    	/*Scheduling work queue*/
    	return IRQ_HANDLED;
    
    }
    
    
    //device driver 작성 부분.
    /*************드라이버 함수 ******************/
    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");
    	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)
    {
    	pr_info("read\n");
    	return 0;
    }
    
    
    static int mydriver_write(struct file *flip,
    		const char *buf, size_t len, loff_t *off)
    {
    	return 0;
    
    }
    
    dev_t dev = 0;
    static struct cdev my_cdev;
    static struct class *dev_class;
    struct my_device_data *main_data;
    static int __init init_hw(void)
    {
    	//디바이스 등록
    	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));
    
    
    	//초기화
    	cdev_init(&my_cdev, &fops);
    	pr_info("[=]driver was initialized\n");
    
    
    	//시스템에 추가
    	if((cdev_add(&my_cdev, dev, 1)) < 0)
    	{
    		pr_err("[!]cannot add device to kernel\n");
    		goto r_del;
    
    	}
    
    
    	//class 만듦.
    	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;
    	}
    
    
    	//gpio 10번을 사용.
    	//export하여 간단히 사용.
    	//입력은 값을 써 넣을 수 없음. 출력으로 설정.
    	GPIO_irqNumber = gpio_to_irq(GPIO_10_OUT);
    	pr_info("[=]irq %d was assinged\n",GPIO_irqNumber);
    
    	//interrupt 등록 필요
    	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");
    
    
    	//메모리 공간 할당.
    	main_data = kmalloc(sizeof(struct my_device_data), GFP_KERNEL);
    
    
    	//spin lock init
    	spin_lock_init(&main_data->lock);
    	main_data->index=0;
    	if(!main_data)
    	{
    		pr_err("[!]cannot alloc memory\n");
    		goto r_memory;
    	}
    	pr_info("[=]got memory\n");
    
    	//thread create.
    	main_data->kthread1 = kthread_create(run_this_thread1, main_data, "my_thread1");
    	main_data->kthread2 = kthread_create(run_this_thread2, main_data, "my_thread2");
    
    	if(main_data->kthread1)
    	{
    		wake_up_process(main_data->kthread1);
    		pr_info("wake up thread1 at %p\n", main_data->kthread1);
    	}
    	if(main_data->kthread2)
    	{
    		wake_up_process(main_data->kthread2);
    		pr_info("wake up thread2 at %p\n", main_data->kthread2);
    	}
    
    	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_memory:
    	;
    
    	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);
    	if(main_data->kthread1)
    		kthread_stop(main_data->kthread1);
    	if(main_data->kthread2)
    		kthread_stop(main_data->kthread2);
    	pr_info("kthread was stopped\n");
    	pr_info("wake up thread1 at %p\n", main_data->kthread1);
    	pr_info("wake up thread2 at %p\n", main_data->kthread2);
    
    
    	kfree(main_data);
    	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!");
    

    spin lock을 적용하지 않을 경우, 겹쳐쓴다. 적용하면 순서대로 실행된다 믿는다.

    [ 1749.848372] ==============
    [ 1749.848376] [+]index is 108
    [ 1749.848380] ==============
    [ 1750.378381] ==============
    [ 1750.378392] [+]index is 109
    [ 1750.378399] ==============
    [ 1750.378414] ==============
    [ 1750.378421] [+]index is 110
    [ 1750.378428] ==============
    [ 1750.898416] ==============
    [ 1750.898431] [+]index is 111
    [ 1750.898439] ==============
    [ 1750.898461] ==============
    [ 1750.898470] [+]index is 112
    [ 1750.898478] ==============
    [ 1751.418404] ==============
    [ 1751.418417] [+]index is 113
    [ 1751.418426] ==============
    [ 1751.418446] ==============
    [ 1751.418455] [+]index is 114
    [ 1751.418463] ==============
    [ 1751.938485] ==============
    [ 1751.938501] [+]index is 115
    [ 1751.938512] ==============
    [ 1751.938537] ==============
    [ 1751.938547] [+]index is 116
    [ 1751.938556] ==============
    [ 1752.458509] ==============
    [ 1752.458529] [+]index is 117
    [ 1752.458538] ==============
    [ 1752.458560] ==============
    [ 1752.458570] [+]index is 118
    [ 1752.458578] ==============
    [ 1752.978388] ==============
    [ 1752.978394] [+]index is 119
    [ 1752.978398] ==============
    [ 1753.498497] kthread was stopped
    [ 1753.498515] wake up thread1 at 39208d2a
    [ 1753.498524] wake up thread2 at 5bdf5278
    [ 1753.498536] module was removed
    [ 1773.404079] [=]236-0, was allocated
    [ 1773.404089] [=]driver was initialized
    [ 1773.406890] [=]irq 54 was assinged
    [ 1773.406942] [=]module was installed
    [ 1773.406952] [=]got memory
    [ 1773.410837] wake up thread1 at 6acc0d97
    [ 1773.410859] wake up thread2 at 9613521a
    [ 1773.411132] ==============
    [ 1773.411143] [+]index is 1
    [ 1773.411151] ==============
    [ 1773.411794] ==============
    [ 1773.411805] [+]index is 2
    [ 1773.411812] ==============
    [ 1773.528622] ==============
    [ 1773.528626] ==============
    [ 1773.528636] [+]index is 4
    [ 1773.528637] [+]index is 4
    [ 1773.528640] ==============
    [ 1773.528641] ==============
    [ 1773.648584] ==============
    [ 1773.648585] ==============
    [ 1773.648589] [+]index is 6
    [ 1773.648590] [+]index is 6
    [ 1773.648593] ==============
    [ 1773.648594] ==============
    [ 1773.768583] ==============
    [ 1773.768584] ==============
    [ 1773.768588] [+]index is 8
    [ 1773.768589] [+]index is 8
    [ 1773.768591] ==============
    [ 1773.768594] ==============
    [ 1773.888583] ==============
    [ 1773.888587] [+]index is 9
    [ 1773.888589] ==============
    [ 1773.888591] ==============
    [ 1773.888595] [+]index is 10
    [ 1773.888598] ==============
    [ 1774.008583] ==============
    [ 1774.008585] ==============
    [ 1774.008588] [+]index is 12
    [ 1774.008591] [+]index is 12
    [ 1774.008594] ==============
    [ 1774.008596] ==============
    [ 1774.128592] ==============
    [ 1774.128593] ==============
    [ 1774.128597] [+]index is 14
    [ 1774.128599] [+]index is 14
    [ 1774.128601] ==============
    [ 1774.128604] ==============
    [ 1774.248635] ==============
    [ 1774.248638] ==============
    [ 1774.248648] [+]index is 16
    [ 1774.248652] [+]index is 16
    [ 1774.248657] ==============
    [ 1774.248663] ==============
    [ 1774.368631] ==============
    [ 1774.368642] [+]index is 17
    [ 1774.368649] ==============
    [ 1774.378613] ==============
    [ 1774.378622] [+]index is 18
    https://embetronicx.com/tutorials/linux/device-drivers/linux-device-drivers-tutorial-kernel-thread/

    https://embetronicx.com/tutorials/linux/device-drivers/spinlock-in-linux-kernel-1/

    https://embetronicx.com/tutorials/linux/device-drivers/linux-device-driver-tutorial-mutex-in-linux-kernel/

  • sync, mutex. unix network programming p161

    다시 thread로 돌아왔다.

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <pthread.h>
    #define	MAXNITEMS 		1000000
    #define	MAXNTHREADS			100
    #define MIN(X,Y) ((X)<(Y) ? (X) : (Y))
    
    
    
    //gcc -lpthread 옵션으로 컴파일.
    int		nitems;			/* read-only by producer and consumer */
    
    
    //shared를 초기화 하는데,
    //맨 처음에 mutext가 있음.
    //나머지는 0으로 초기화됨..
    struct {
      pthread_mutex_t	mutex;
      int	buff[MAXNITEMS];
      int	nput;
      int	nval;
    } shared = { PTHREAD_MUTEX_INITIALIZER };
    
    void	*produce(void *), *consume(void *);
    
    
    int
    main(int argc, char **argv)
    {
    	int			i, nthreads, count[MAXNTHREADS];
    	pthread_t	tid_produce[MAXNTHREADS], tid_consume;
    	//앞에서부터 초기화.
    	struct test {
    		int no1;
    		int no2;
    	} teststruct = {10};
    
    	if (argc != 3)
    		perror("usage: prodcons2 <#items> <#threads>");
    	nitems = MIN(atoi(argv[1]), MAXNITEMS);
    	nthreads = MIN(atoi(argv[2]), MAXNTHREADS);
    
    	//pthread_setconcurrency();
    		/* 4start all the producer threads */
    	for (i = 0; i < nthreads; i++) {
    		count[i] = 0;
    		pthread_create(&tid_produce[i], NULL, produce, &count[i]);
    	}
    
    		/* 4wait for all the producer threads */
    	for (i = 0; i < nthreads; i++) {
    		pthread_join(tid_produce[i], NULL);
    		printf("count[%d] = %d\n", i, count[i]);	
    	}
    
    		/* 4start, then wait for the consumer thread */
    	pthread_create(&tid_consume, NULL, consume, NULL);
    	pthread_join(tid_consume, NULL);
    
    	exit(0);
    }
    /* end main */
    
    /* include producer */
    void *
    produce(void *arg)
    {
    	for ( ; ; ) {
    		pthread_mutex_lock(&shared.mutex);
    		if (shared.nput >= nitems) {
    			pthread_mutex_unlock(&shared.mutex);
    			return(NULL);		/* array is full, we're done */
    		}
    		shared.buff[shared.nput] = shared.nval;
    		shared.nput++;
    		shared.nval++;
    		pthread_mutex_unlock(&shared.mutex);
    		*((int *) arg) += 1;
    	}
    }
    
    void *
    consume(void *arg)
    {
    	int		i;
    
    	for (i = 0; i < nitems; i++) {
    		if (shared.buff[i] != i)
    			printf("buff[%d] = %d\n", i, shared.buff[i]);
    	}
    	return(NULL);
    }
    /* end producer */