Read the printk code

Source: Internet
Author: User

Read the printk code

We usually use printk when writing kernel code. here we can see how printk is associated with UART. I will not introduce hardware related to UART, so easy. Here we just read the printk code to see the association with the UART driver.

Printk --- the function is implemented in the kernel/printk. c file. Many of the following functions are in this file.

This is a magic function. The parameter "..." can make your parameter input more casual. Declare a va_list in the function body, and use the va_start function to obtain the parameters in the parameter list. Call va_end () to complete the use.

asmlinkage int printk(const char *fmt, ...){va_list args;int r;#ifdef CONFIG_KGDB_KDBif (unlikely(kdb_trap_printk)) {va_start(args, fmt);r = vkdb_printf(fmt, args);va_end(args);return r;}#endifva_start(args, fmt);_trace_kernel_printk(_RET_IP_);r = vprintk(fmt, args);va_end(args);return r;}

Here is the Data normalization, and finally goes to the vprintk function.

Asmlinkage int vprintk (const char * FMT, va_list ARGs ){........................ spin_lock (& logbuf_lock); // apply for a lock on the log buffer, the use of the lock has a good effect on preventing concurrency ........................ /* Here we will see the processing of the input log level */If (P [0] = '<') {unsigned char c = P [1]; if (C & P [2] = '>') {Switch (c) {Case '0 '... '7':/* loglevel */current_log_level = C-'0';/* fallthrough-make sure we're on a new line */case 'D':/* kern_default */If (! New_text_line) {emit_log_char ('\ n'); new_text_line = 1;}/* fallthrough-Skip the loglevel */case 'C':/* kern_cont */P + = 3; break ;}}/ ** copy the content to be output to log_buf. Then determine whether to print the log tag and timestamp. */For (; * P; P ++) {If (new_text_line) {/* always output the token */emit_log_char ('<'); emit_log_char (current_log_level + '0'); emit_log_char ('>'); printed_len + = 3; new_text_line = 0; If (printk_time) {/* Follow the token with the time */Char tbuf [50], * TP; unsigned tlen; unsigned long T; unsigned long nanosec_rem; t = cpu_clock (printk_cpu ); nanosec_rem = do_div (T, 1000000000); tlen = sprintf (tbuf ,"[ % 5lu. % 06lu] ", (unsigned long) T, nanosec_rem/1000); For (TP = tbuf; TP <tbuf + tlen; TP ++) emit_log_char (* TP ); printed_len + = tlen;} If (! * P) break;} emit_log_char (* P); If (* P = '\ n') new_text_line = 1 ;} /** obtain the console usage mark and release it immediately. The release function will perform the output action. ** in the previous figure, you can see the spin_lock (& logbuf_lock ), the function acquire_lele_semaphore_for_printk will also be released * reiterate: Remember to release the lock */If (acquire_console_semaphore_for_printk (this_cpu )) release_console_sem ();............................}

Now let's take a look at how release_lele_sem achieves the output.
Release_lele_sem is used to unlock the Console System.
 

Void release_console_sem (void) {// first checks whether the system needs to sleep. If it needs to sleep, it leaves for (;) {spin_lock_irqsave (& logbuf_lock, flags ); wake_klogd | = log_start-log_end; If (con_start = log_end) break;/* nothing to print */_ con_start = con_start; _ log_end = log_end; con_start = log_end; /* flush */spin_unlock (& logbuf_lock); stop_critical_timings ();/* Don't trace print latency * // after the data is taken out, here we finally see the call to the serial port driver interface call_console_drivers (_ con_start, _ log_end); then (); local_irq_restore (flags);} console_locked = 0; up (& console_sem ); spin_unlock_irqrestore (& logbuf_lock, flags); If (wake_klogd) wake_up_klogd ();} export_symbol (release_console_sem );

So, from the above function, let's take a look at the path in the call_console_drivers function.

/** To call the console driver, first extract the data from log_buf [start] To log_buf [end-1]. * In this process, keep the console flag in the */static void call_console_drivers (unsigned start, unsigned end) {unsigned cur_index, start_print; static int msg_level =-1; bug_on (INT) (START-end)> 0); cur_index = start; start_print = start; while (cur_index! = END) {If (msg_level <0 & (end-cur_index)> 2) & log_buf (cur_index + 0) = '<' & log_buf (cur_index + 1)> = '0' & log_buf (cur_index + 1) <= '7' & log_buf (cur_index + 2) = '>') {msg_level = log_buf (cur_index + 1)-'0'; cur_index + = 3; start_print = cur_index;} while (cur_index! = END) {char c = log_buf (cur_index); cur_index ++; If (C = '\ n') {If (msg_level <0) {/** printk () has already given us loglevel tags in * the buffer. this code is here in case the * log buffer has wrapped right round and scribbled * on those tags */msg_level = default_message_loglevel;} _ call_console_drivers (start_print, cur_index, msg_level ); msg_level =-1; start_print = cur_index; break ;}}_ call_lele_drivers (start_print, end, msg_level );}

We haven't seen it yet, but we can see the more in-depth interface _ call_console_drivers. Let's look at this function.

static void _call_console_drivers(unsigned start,unsigned end, int msg_log_level){if ((msg_log_level < console_loglevel || ignore_loglevel) &&console_drivers && start != end) {if ((start & LOG_BUF_MASK) > (end & LOG_BUF_MASK)) {/* wrapped write */__call_console_drivers(start & LOG_BUF_MASK,log_buf_len);__call_console_drivers(0, end & LOG_BUF_MASK);} else {__call_console_drivers(start, end);}}}

What follows _ call_lele_drivers?

static void __call_console_drivers(unsigned start, unsigned end){struct console *con;for_each_console(con) {if ((con->flags & CON_ENABLED) && con->write &&(cpu_online(smp_processor_id()) ||(con->flags & CON_ANYTIME)))con->write(con, &LOG_BUF(start), end - start);}}

Do you think you can't find you after hiding it? It's useless. You are a function that pulls the wind. No matter where you are, it's like a firefly in the dark. It's so clear, so outstanding, and your melancholy eyes, hu xiaozi, the miracle of the knife, and the cup of dry Martine, are deeply fascinated by me. However, even if you are doing this well, but you have some rules, I want to analyze your inner con-> write.

Now, let's take a look at the structure of the boom.

struct console {charname[16];void(*write)(struct console *, const char *, unsigned);int(*read)(struct console *, char *, unsigned);struct tty_driver *(*device)(struct console *, int *);void(*unblank)(void);int(*setup)(struct console *, char *);int(*early_setup)(void);shortflags;shortindex;intcflag;void*data;struct console *next;};

Haha, you must have noticed that there is write here. Yes, this is called in con-> write above.
In your UART driver, you must have seen register_console (& arch-xxx_uart_console); this function, the write inside is registered here for printk to use. The implementation in write is the small cases of UART.
Of course there is also this console_initcall (arch-xxx_uart_console_init); to add your UART.

You might notice that the READ function is useless when looking at the struct definition of the arch-xxx_uart_console? Why?
"When the TTY driver receives the data, it is responsible for passing any data obtained from the hardware to the TTY core, instead of using the traditional READ function. Tty core directly sends buffered data to requests from users. Because the TTY core already provides buffer logic, there is no need to implement their own buffer logic for each tty driver .". Of course, this is what the textbook says. In the future, we will read this part of the source code.

Of course, printk does not have to be output by serial port. Here is just an example. Today we will first go here. Next we will show you how to specify the output of printk. Here we will talk about _ call_lele_drivers () the for_each_console () in the function is critical. See the next article.

Have fun!

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.