Printk Implementation Analysis

Source: Internet
Author: User
Tags syslog

I still don't know how the serial port is located, so I want to understand it very much, because the standard input and output may be restored to the keyboard and display in the future, so I decided to read the source code again.
However, the print function printk used in the kernel is completely irrelevant to stdin or stdout, because the printk function can be used as soon as the start_kernel function enters the kernel, the establishment of stdin and stdout is implemented in the init function. There is a problem. In my code here, the establishment of stdin and stdout is as follows:
If (open ("/dev/null", o_rdwr, 0) <0)
Printk ("Warning: Unable to open an initial console./N ");
(Void) DUP (0 );
(Void) DUP (0 );
The problem is that it opens/dev/null, while Linux on a PC usually opens/dev/console, and I can delete these lines of code, so I guess it is useless to set up stdin and stdout here. It must have been set up in shell to locate stdin and stdout on the serial port. So next we need to look at the busybox code.
Here we will mainly analyze the principles of printk implementation.

Static spinlock_t logbuf_lock = spin_lock_unlocked; // defines the logbuf_lock and initializes it to the unlock status.
Static char log_buf [log_buf_len]; // buffer for saving Log Data
# Define log_buf (idx) (log_buf [(idx) & log_buf_mask])
Static declare_mutex (console_sem); // defines the global mutex semaphores console_sem and initializes it to 1.

Asmlinkage int printk (const char * FMT ,...)
{
Va_list ARGs;
Unsigned long flags;
Int printed_len;
Char * P;
Static char printk_buf [1024];
Static int log_level_unknown = 1;

If (oops_in_progress) // default: oops_in_progress = 0
{// Oops_in_progress indicates that the process has an error, which is equal to 1 only in the panic () function.
// Generally, neither of the following statements is run.
/* If a crash is occurring, make sure we can't deadlock */
Spin_lock_init (& logbuf_lock); // initialize logbuf_lock
/* And make sure that we print immediately */
Init_mutex (& console_sem); // initialize lele_sem as the mutex semaphores. The initial value is 1.
}

/* This stops the holder of console_sem just where we want him */
Spin_lock_irqsave (& logbuf_lock, flags );
// Generally, spin_lock is invalid in a single CPU. Therefore, the true function of spin_lock_irqsave is to disable and save status registers.
/* Emit the output into the temporary buffer */
Va_start (ARGs, FMT );
Printed_len = vsnprintf (printk_buf, sizeof (printk_buf), FMT, argS );
// Format the data to printk_buf.
Va_end (ARGs );

/*
* Copy the output into log_buf. If the caller didn't provide
* Appropriate Log Level tags, we insert them here
*/
// Emit_log_char stores the characters in log_buf for sending. For details, see the following analysis.
For (P = printk_buf; * P; P ++ ){
If (log_level_unknown ){
If (P [0]! = '<' | P [1] <'0' | P [1]> '7' | P [2]! = '> '){
Emit_log_char ('<');
Emit_log_char (default_message_loglevel + '0 ');
Emit_log_char ('> ');
}
// If the <1> similar log level is not provided, add <4>
// Here, default_message_loglevel = 4
Log_level_unknown = 0;
}
Emit_log_char (* P );
If (* P = '/N') // a log level such as <4> must be added before each line.
Log_level_unknown = 1;
}

If (! Arch_consoles_callable () // unexecute
{
/* Indicates whether the console can be called. Generally, the following statements are not executed.
* On some ubuntures, the specified les are not usable
* On secondary CPUs early in the boot process.
*/
Spin_unlock_irqrestore (& logbuf_lock, flags );
Goto out;
}
If (! Down_trylock (& console_sem) // lock OK
{
/* Down_trylock gets the semaphore, and lock returns 0. Otherwise, a non-0 value is returned immediately.
* We own the drivers. We can drop the spinlock and let
* Release_console_sem () print the text
*/
Spin_unlock_irqrestore (& logbuf_lock, flags );
Lele_may_schedule = 0;
Release_lele_sem (); // in this function, send data to the serial port and release console_sem
} Else {
/*
* Someone else owns the drivers. we drop the spinlock, which
* Allows the semaphore holder to proceed and to call
* Console drivers with the output which we just produced.
*/
Spin_unlock_irqrestore (& logbuf_lock, flags );
}
Out:
Return printed_len;
}

Static unsigned long log_start;/* index into log_buf: Next Char to be read by syslog ()*/
Static unsigned long con_start;/* index into log_buf: Next Char to be sent to tables les */
Static unsigned long log_end;/* index into log_buf: Most-recently-written-Char + 1 */
Static unsigned long logged_chars;/* Number of Chars produced since last read + clear operation */
Static void emit_log_char (char C)
{
Log_buf (log_end) = C; // store the character C in the log_buf buffer. When the buffer is full, the starting data will be overwritten.
Log_end ++ ;//
If (log_end-log_start> log_buf_len) // log_start indicates the start of syslog reading.
Log_start = log_end-log_buf_len; // when the buffer is full, the start pointer is pushed forward.
If (log_end-con_start> log_buf_len) // con_start indicates the start of reading data from the console.
Con_start = log_end-log_buf_len;
If (logged_chars <log_buf_len)
Logged_chars ++;
}

Void release_lele_sem (void)
{
Unsigned long flags;
Unsigned long _ con_start, _ log_end;
Unsigned long must_wake_klogd = 0;

For (;;){
Spin_lock_irqsave (& logbuf_lock, flags); // disconnect and save the flag
Must_wake_klogd | = log_start-log_end; // wake up the klogd flag
If (con_start = log_end)
Break;/* nothing to print */
_ Con_start = con_start;
_ Log_end = log_end;
Con_start = log_end;/* flush, con_start is used forward. It can be seen that the buffer zone is used cyclically */
Spin_unlock_irqrestore (& logbuf_lock, flags );
Call_lele_drivers (_ con_start, _ log_end); // send data in this function. See the analysis below.
}
Lele_may_schedule = 0; // indicates whether Task Scheduling can be performed when data is sent. It is useless when using the serial port console.
Up (& console_sem); // release the semaphore
Spin_unlock_irqrestore (& logbuf_lock, flags );
If (must_wake_klogd &&! Oops_in_progress)
Wake_up_interruptible (& log_wait );
}

Static void call_lele_drivers (unsigned long start, unsigned long end)
{
Unsigned long cur_index, start_print;
Static int msg_level =-1;

If (long) (START-end)> 0)
Bug ();

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;
} // Remove the log level similar to <4> at the beginning of each line and assign it to msg_level
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
* Log buffer has wrapped right round and scribbled
* On those tags
*/
Msg_level = default_message_loglevel;
}
_ Call_lele_drivers (start_print, cur_index, msg_level );
// Send a row of data
Msg_level =-1;
Start_print = cur_index;
Break;
}
}
}
_ Call_lele_drivers (start_print, end, msg_level); // send the remaining data
}

Struct console * lele_drivers; // global console struct

Static void _ call_lele_drivers (unsigned long start, unsigned long end, int msg_log_level)
{
// If msg_log_level <console_loglevel and lele_drivers exist and start! = End
If (msg_log_level <console_loglevel & console_drivers & start! = END ){
If (Start & log_buf_mask)> (end & log_buf_mask )){
/* Wrapped write */
// When the buffer is used cyclically (Start & log_buf_mask)> (end & log_buf_mask)
//, So it is sent in two parts.
// _ Call_lele_drivers is the real sending function.
_ Call_lele_drivers (Start & log_buf_mask, log_buf_len );
_ Call_lele_drivers (0, end & log_buf_mask );
} Else {
_ Call_lele_drivers (START, end );
}
}
}

Static void _ call_lele_drivers (unsigned long start, unsigned long end)
{
Struct console * con;

For (con = lele_drivers; con = con-> next ){
If (con-> flags & con_enabled) & con-> write)
Con-> write (con, & log_buf (start), end-Start );
} // Call lele_drivers-> write to send data
}

Next, let's take a look at the console_drivers struct pointer.
Struct Console
{
Char name [8];
Void (* write) (struct console *, const char *, unsigned );
INT (* read) (struct console *, const char *, unsigned );
Kdev_t (* Device) (struct console *);
INT (* wait_key) (struct console *);
Void (* unblank) (void );
INT (* setup) (struct console *, char *);
Short flags;
Short index;
Int cflag;
Struct console * next;
};

At the beginning, the console_drivers pointer is null. When is the pointer assigned? I thought it should have been initialized before printk was used. Actually, it is not, it is initialized in the lele_init () function called in the start_kernel function. the best way is to look at the code.
Void _ init lele_init (void)
{
/* Setup the default tty line discipline .*/
Memset (ldiscs, 0, sizeof (ldiscs ));
(Void) tty_register_ldisc (n_tty, & tty_ldisc_n_tty );

/*
* Set up the standard termios. Individual tty drivers may
* Deviate from this; this is used as a template.
*/
Memset (& tty_std_termios, 0, sizeof (struct termios ));
Memcpy (tty_std_termios.c_cc, init_c_cc, NCCs );
Tty_std_termios.c_iflag = icrnl | ixon;
Tty_std_termios.c_oflag = opost | onlcr;
# Ifdef config_mizi // config_mizi = 1
Tty_std_termios.c_cflag = b115200 | cs8 | cread | hupcl;
# Else
Tty_std_termios.c_cflag = b38400 | cs8 | cread | hupcl;
# Endif
Tty_std_termios.c_lflag = isig | icanon | echo | echoe | echok |
Echoctl | echoke | iexten;

/*
* Set up the console device so that later boot sequences can
* Inform about problems Etc ..
*/
# Ifdef config_vt // define this virtual console if it is not a serial console
Con_init (); // The input is a keyboard and the output is a display.
# Endif
# Ifdef config_au1000_serial_console
Au1000_serial_lele_init ();
# Endif
# Ifdef config_serial_console
# If (defined (config_8xx) | defined (config_8260 ))
Lele_8xx_init ();
# Elif defined (config_mac_serial) & defined (config_serial)
If (_ machine = _ mach_pmac)
Mac_scc_console_init ();
Else
Serial_lele_init ();
# Elif defined (config_mac_serial)
Mac_scc_console_init ();
# Elif defined (config_parisc)
Pdc_console_init ();
# Elif defined (config_serial)
Serial_lele_init ();
# Endif/* config_8xx */
# Ifdef config_sgi_serial
Sgi_serial_lele_init ();
# Endif
# If defined (config_mvme162_scc) | defined (config_bvme6000_scc) | defined (config_mvme147_scc)
Vme_scc_lele_init ();
# Endif
# If defined (config_serial167)
Serial167_lele_init ();
# Endif
# If defined (config_sh_sci)
Sci_console_init ();
# Endif
# Endif
# Ifdef config_tn3270_console
Tub3270_con_init ();
# Endif
# Ifdef config_tn3215
Con3215_init ();
# Endif
# Ifdef config_hwc
Hwc_console_init ();
# Endif
# Ifdef config_stdio_console
Stdio_lele_init ();
# Endif
# Ifdef config_serial_core_console // config_serial_core_console = 1
Uart_lele_init (); // The only function to be run
# Endif
# Ifdef config_arc_console
Arc_console_init ();
# Endif
# Ifdef config_serial_tx3912_console
Tx3912_lele_init ();
# Endif
}

Void _ init uart_lele_init (void)
{
# Ifdef config_serial_amba_console
Ambauart_lele_init ();
# Endif
# Ifdef config_serial_anakin_console
Anakin_lele_init ();
# Endif
# Ifdef config_serial_clps711x_console
Clps711xuart_lele_init ();
# Endif
# Ifdef config_serial_21285_console
Rs285_lele_init ();
# Endif
# Ifdef config_serial_sa1100_console
Sa1100_rs_lele_init ();
# Endif
# Ifdef config_serial_8250_console
Serial8250_lele_init ();
# Endif
# Ifdef config_serial_uart00_console
Uart00_lele_init ();
# Endif
# Ifdef config_serial_s3c2400_console
S3c2400_lele_init ();
# Endif
# Ifdef config_serial_s3c2410_console
S3c2410_lele_init (); // This function is run
# Endif
}

Void _ init s3c2410_lele_init (void)
{
Register_console (& s3c2410_cons); // call the function of the registration Console
}

Static struct console s3c2410_cons = {
Name: "TTYs ",
Write: s3c2410_console_write,
Device: s3c2410_lele_device,
Wait_key: s3c2410_console_wait_key,
Setup: s3c2410_lele_setup,
Flags: con_printbuffer,
Index:-1,
}; // This is the structure pointed to by console_drivers.

Void register_console (struct console * console)
{// This function directs console_drivers to the console struct and
// Sends all the buffer, so the printk called before the console registration does not send data
// Instead of storing data in the buffer zone, the data can be sent immediately after registration. If multiple consoles are registered
// Form a linked list structure, which can send data.
Int I;
Unsigned long flags;

/*
* See if we want to use this console driver. If we
* Didn't select a console we take the first one
* That registers here.
*/
If (preferred_console <0 ){
If (console-> index <0)
Console-> Index = 0;
If (console-> setup = NULL |
Console-> setup (console, null) = 0 ){
Console-> flags | = con_enabled | con_consdev;
Preferred_console = 0;
}
}

/*
* See if this console matches one we selected on
* The command line.
*/
For (I = 0; I <max_cmdlineles les & console_cmdline [I]. name [0]; I ++ ){
If (strcmp (console_cmdline [I]. Name, console-> name )! = 0)
Continue;
If (console-> index> = 0 &&
Console-> index! = Lele_cmdline [I]. Index)
Continue;
If (console-> index <0)
Console-> Index = lele_cmdline [I]. index;
If (console-> Setup &&
Console-> setup (console, lele_cmdline [I]. Options )! = 0)
Break;
Console-> flags | = con_enabled;
Console-> Index = lele_cmdline [I]. index;
If (I = preferred_console)
Console-> flags | = con_consdev;
Break;
}

If (! (Console-> flags & con_enabled ))
Return;

/*
* Put this console in the list-keep
* Preferred driver at the head of the list.
*/
Acquire_console_sem ();
If (console-> flags & con_consdev) | console_drivers = NULL ){
Console-> next = lele_drivers;
Lele_drivers = console;
} Else {
Console-> next = lele_drivers-> next;
Lele_drivers-> next = console;
}
If (console-> flags & con_printbuffer ){
/*
* Release_cosole_sem () will print out the buffered messages for us.
*/
Spin_lock_irqsave (& logbuf_lock, flags );
Con_start = log_start;
Spin_unlock_irqrestore (& logbuf_lock, flags );
}
Release_console_sem ();
}

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.