Kernel thread Analysis for Linux driver Learning

Source: Internet
Author: User
Document directory
  • 3.1 manage and schedule other kernel threads kthread

The difference between a kernel thread and a common process is that the kernel thread does not have an independent address space. It runs only in the kernel space and never switches to the user space. It is the same as a common process, can be scheduled or preemptible.

 

One-thread Creation
Struct task_struct * kthread_create (INT (* threadfn) (void * data), void * data, const char namefmt [],...); after a thread is created, it does not run immediately. Instead, it must pass the task_struct pointer returned by kthread_create () to wake_up_process () to drive the thread. You can also use kthread_run to create a thread and start the thread struct task_struct * kthread_run (INT (* threadfn) (void * data), void * data, const char * namefmt ,...); # define kthread_run (threadfn, Data, namefmt ,...) \ ({\ struct task_struct * _ k \ = kthread_create (threadfn, Data, namefmt, ##_ _ va_args _); \ If (! Is_err (_ k) \ wake_up_process (_ k) ;\__ K ;\})

It can be seen that kthread_run runs wake_up_process after kthread_create is called.
To call kernel_thread in a non-kernel thread, you must call daemonize (...) to release resources and become a real kernel thread. kthread_create actually calls kernel_thread but has done internal processing and does not need to call daemonize on its own.

Two-thread exit

Kthread_stop: Set the exit mark of the thread (the int kthread_should_stop (void) function is applied in the thread function, and the function should be exited when the return is true). kthread_stop will wait until the thread ends, before the end of the thread, kthread_stop is sent. If do_exit is directly used to exit the thread, kthread_stop will not receive the completion signal and will wait. If the thread has exited, kthread_stop will first set the exit mark and then wake up the thread. After the thread is awakened, kthread_stop will judge the exit mark so the set handler function will not be called. If the thread has been awakened and exited, kthread_stop will wait.

Int kthread_stop (struct task_struct * thread); If the processing function does not use kthread_should_stop to determine the exit, kthread_stop will wait until the processing function exits.

The kernel version used here is 2.6.21.5.
3.1 manage and schedule other kernel threads kthread

Run the ps command to check that a process named kthread was created during kernel initialization.

Static _ init int helper_init (void) {// create a single-threaded shared queue helper_wq = create_singlethread_workqueue ("kthread"); bug_on (! Helper_wq); Return 0;} core_initcall (helper_init );

The shared queue kthread_create defines a job and creates a specific thread in the job.

3.2 kthread_create creation thread

Let's take a look at the kthread_create_info structure before looking at kthread_create. It is used when each thread is created.

Struct kthread_create_info {/* Information passed to kthread () from keventd. */INT (* threadfn) (void * data); // void * Data of the thread processing function; // The thread parameter struct completion started; // wait for the kernel_thread thread to be created during work. After the thread is created, the thread will notify the job to continue. /* Result passed back to kthread_create () from keventd. */struct task_struct * result; // started is used to store the created task struct completion done after receiving the started signal created by the thread; // after a worker thread joins a job, it will wait until the job is finished. This job is only used to create a thread. Struct work_struct work; // create a thread. For details, refer to the following source code };

/*** Kthread_create-create a thread. * @ threadfn: the function to run until signal_pending (current ). * @ data: Data PTR for @ threadfn. * @ namefmt: printf-style name for the thread. ** Description: This helper function creates and names a kernel thread. It does not run after the thread is created. It runs using the wake_up_process () function. For details, refer to kthread_run (), kthread_create_on_cpu () ** after being awakened, the thread calls the threadfn () function data as the parameter. If the independent thread does not call kthread_stop (), you can directly use do_exit (), or when kthread_should_stop () is detected to return true (kthread_stop () Has been called) return processing function, should return 0 or a negative number, the return value will be passed to kthread_stop () to return. */Struct task_struct * kthread_create (INT (* threadfn) (void * data), void * data, const char namefmt [],...) {struct kthread_create_info create; // The following five elements initialize kthread_create_infocreate.threadfn = threadfn; create. data = data; init_completion (& create. started); init_completion (& create. done); init_work (& create. work, keventd_create_kthread); // you can see that the created job is performed in the keventd_create_kthread function/* The workqueue needs to start up f IRST: */If (! Helper_wq) // after the system is started, it is normally initialized with create. work. func (& create. work); // if it is not initialized, it is only finished in the current process, rather than in the kthread else {queue_work (helper_wq, & create. work); // Add the work to the queue and schedule wait_for_completion (& create. done); // wait until the work is finished. After the execution, create. result: The structure or error of the created task is returned. because the task is executed in kthread, you must wait until the task is finished before returning.} If (! Is_err (create. result) {va_list ARGs; va_start (ARGs, namefmt); vsnprintf (create. result-> comm, sizeof (create. result-> comm), namefmt, argS); va_end (ARGs);} return create. result ;}

The above shows that the creation work is in the keventd_create_kthread function, then look at the keventd_create_kthread Function

/* We are keventd: Create a thread. this function works in the keventd kernel thread */static void keventd_create_kthread (struct work_struct * Work) {struct kthread_create_info * Create = container_of (work, struct kthread_create_info, work); int PID; /* we want our own signal handler (we take no signals by default) * // * we use our own signal processing. By default, no signal is processed */PID = kernel_thread (kthread, create, clone_fs | clone_files | sigchld); // create a function here. The thread processing function is kthrea. D function. The parameter is the create pointer of struct kthread_create_info. If (PID <0) {create-> result = err_ptr (PID);} else {wait_for_completion (& create-> started); // wait for the Creation thread to execute, after the thread executes the task, it will send the completion signal create-> startedread_lock (& tasklist_lock); Create-> result = find_task_by_pid (PID); read_unlock (& tasklist_lock );} complete (& create-> done );}

At this time, kthread_create is waiting for the create-> done signal, and the kernel thread keventd is waiting for the thread to create-> started. The thread is created above, and the processing function is kthread.

Static int kthread (void * _ create) {struct kthread_create_info * Create = _ create; int (* threadfn) (void * data); void * data; sigset_t blocked; int ret =-eintr; kthread_exit_files ();/* Copy Data: It's on keventd's stack */threadfn = create-> threadfn; Data = create-> data; /* block and flush all signals (in case we're not from keventd ). block all signals */sigfillset (& blocked); sigprocmask (sig_block, & blocked, null); flush_s Ignals (current);/* by default we can run anywhere, unlike keventd. allow threads to run keventd on any CPU. The value */set_cpus_allowed (current, cpu_mask_all);/* OK, tell user we're spawned, wait for stop or wakeup */_ set_current_state (task_interruptible); Complete (& create-> started); // notify keventd to complete thread initialization, after receiving the task structure of the new thread, keventd returns kthread_create. Schedule (); If (! Kthread_should_stop () // determine whether the previously called kthread_stopret = threadfn (data); // The defined thread function is actually executed here/* It might have exited on its own, w/O kthread_stop. check. */If (kthread_should_stop () {// determine whether kthread_stopkthread_stop_info.err = RET has been executed; // RET is the return of the thread function, and will return complete (& kthread_stop_info.done) through the kthread_stop ); // If kthread_stop is executed and the kthread_stop thread ends, if do_exit is used by the user-defined processing function, kthread_stop is not notified, causing kthread_stop to wait .} Return 0 ;}

Now we can see how kthread_create creates a thread and how the thread works.

3.3 stop the kthread_stop thread

First, let's look at the Stop structure.

Struct kthread_stop_info {struct task_struct * k; // thread structure int err to be stopped; // return value struct completion done; // wait signal for completion of thread }; /* thread stopping is done by setthing this var: Lock serializes multiple kthread_stop CILS. * // * The kthread_stop lock can be called by only one thread at a time in the system */static define_mutex (kthread_stop_lock); static struct kthread_stop_info;

/** * kthread_should_stop - should this kthread return now? * When someone calls kthread_stop() on your kthread, it will be woken * and this will return true.  You should then return, and your return * value will be passed through to kthread_stop(). */int kthread_should_stop(void){return (kthread_stop_info.k == current);}

This function returns true after kthread_stop () is called. When the return value is true, Your processing function will return the result. The return value will be returned through kthread_stop. Therefore, your processing function should have the code to judge kthread_should_stop and then exit.

/*** Kthread_stop-stop a thread created by kthread_create (). * @ k: thread created by kthread_create (). ** sets kthread_should_stop () for @ k to return true, wakes it, and * waits for it to exit. your threadfn () must not call do_exit () * itself if you use this function! This can also be called after * kthread_create () instead of calling wake_up_process (): The thread * will exit without calling threadfn (). ** returns the result of threadfn (), or %-eintr if wake_up_process () * was never called. */INT kthread_stop (struct task_struct * k) {int ret; mutex_lock (& kthread_stop_lock); // The system can only process one end thread request at a time/* It cocould exit after stop_info.k set, but before wake_up_process. */Ge T_task_struct (k); // increase the thread reference count/* Must init completion * Before * thread sees kthread_stop_info.k */init_completion (& kthread_stop_info.done); smp_wmb (); /* now set kthread_should_stop () to true, and wake it up. */kthread_stop_info.k = K; // after this is set, kthread_should_stop () will return true wake_up_process (k ); // no matter whether the thread is running or not, wake up first (if the thread has been awakened and ended, the thread will not be able to wake up, which will cause the following to wait for the kthread_stop_info.done signal ), user-defined functions are not run even after the wake-up operation is not performed. Put_task_struct (k);/* once it dies, reset stop PTR, gather result and we're all. */wait_for_completion (& kthread_stop_info.done); // wait for the thread to finish kthread_stop_info.k = NULL; ret = success; // return value mutex_unlock (& kthread_stop_lock); return ret ;}

NOTE: If kthread_stop is called, your handler cannot call do_exit (), and the function returns the return value of your handler. If the created thread has not called wake_up_process (),-eintr is returned.

4. Test code

Struct task_struct * mytask;/* the Code must contain kthread_should_stop () to determine whether the returned value is meaningful only to kthread_stop. */INT func (void * Data) {While (1) {If (kthread_should_stop () Return-1; printk (kern_alert "func running \ n"); set_current_state (task_uninterruptible); schedule_timeout (1 * Hz);} return 0 ;} thread creation and driver mytask = kthread_create (func, 0, "mykthread"); wake_up_process (mytask); Call kthread_stop (mytask) where it needs to be terminated );

Several functions can easily create kernel threads, but after the threads are created, we are more concerned with concurrency and competition issues caused by multithreading. Concurrent management is one of the core issues in operating system programming. The errors caused are some of the most common and difficult to find.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.