Linux TTY函数跟踪


1. 介绍

本文介绍了TTY打开、TTY读和TTY写操作的函数跟踪过程

2. 示例

下面是一个简单的Linux TTY打开和读写过程

#include 
#include 
#include <string.h>
#include 
#include 

int main()
{
    struct termios toptions;
    int fd, falgs;

    flags = O_RDWR | O_NOCTTY | O_SYNC | O_NDELAY;
    fd = open("/dev/ttyS0", flags);

    tcflush(fd, TCIOFLUSH);
    memset(&toptions, 0, sizeof(toptions));
    tcgetattr(fd, &toptions);
    cfmakeraw(&toptions);

tcgetattr(fd, &toptions); cfsetospeed(&toptions, B115200); cfsetispeed(&toptions, B115200); tcgetattr(fd, &toptions); toptions.c_cflag = B115200 | CRTSCTS | CS8 | CLOCAL | CREAD | INPCK; toptions.c_cc[VMIN] = 1; toptions.c_cc[VTIME] = 0; tcflush(fd, TCIOFLUSH); tcsetattr(fd, TCSANOW, &toptions); uint8_t data[64] = { 0x01, 0x09, 0x10, 0x00 }; write(fd, data, 4); memset(read_data, 0, 64); read(fd, data, 64); return 0; }

3. 打开

open会调用tty_open, 其分析如下:

tty_open
  tty_alloc_file
  /* 分配tty_file_private并赋值给file::private_data */
  tty_open_current_tty
  /* 如果当前设备是/dev/tty则尝试重新打开, 通常返回NULL */
  tty_open_by_driver
  /* 如果上面的函数返回NULL通过查找tty驱动打开tty设备 */
    tty_lookup_driver
    /* 根据设备号查找对应的tty驱动 */
    tty_driver_lookup_tty
    /* 根据tty_driver::tty_operations::lookup/tty_driver::tty_struct查找tty_struct是否存在 */
    tty_reopen
    /* 如果tty_struct已分配, 则打开该tty */
      tty_ldisc_reinit
      /* 初始化tty设备的线路规程 */
        tty_ldisc_get
        /* 根据类别(默认为N_TTY)获取注册的对应线路规程, 并分配对应tty_ldisc实例 */
        tty_set_termios_ldisc
        /* 设置该tty的线路规程 */
        tty_ldisc_open
        /* 调用tty_ldisc::tty_ldisc_ops::open, 对于N_TTY为n_tty_open */
    tty_init_dev
    /* 如果tty_struct没有分配则进行分配(!!!uart就是如此!!!) */
      alloc_tty_struct
      /* 分配和初始化一个tty_struct, 包括driver, ops, index等 */
        tty_ldisc_init
        /* 初始化tty_struct的线路规程 */
          tty_ldisc_get
          /* 获取N_TTY对应线路规程, 并分配对应tty_ldisc实例 */ 
        tty_struct::tty_operations = tty_driver::tty_operations
        /* 设置tty_struct的函数操作集 */
      tty_driver_install_tty
      /* 通过tty_driver::tty_operations::install或者tty_standard_install为tty设备安装入口 */
      tty_ldisc_setup
      /* 打开线路规程 */
        tty_ldisc_open
        /* 调用tty_ldisc::tty_ldisc_ops::open, 对于N_TTY为n_tty_open */
  tty_add_file
  /* 关联文件到tty_struce */
  tty_struct::tty_operations::open
  /* 同tty_driver::tty_operations::open, 对于串口即uart_open */

uar_open分析如下

uart_open 
  tty_port_open
    uart_port_activate
    /* 即tty_port::tty_port_operations::activate */
      uart_startup
        uart_port_startup
          uart_change_pm
          /* 调用uart_port::uart_ops::pm */
          get_zeroed_page
          /* 分配一页并分配给uart_state.xmit.buf */
          uart_circ_clear
          /* 初始化TX环形缓冲区头和尾指针 */ 
          uart_port::uart_ops::startup
          uart_change_speed

4. 写入

write会调用tty_write, 其分析如下:

tty_write
  file_tty
  /* 通过file::tty_file_private::tty_struct获取对应的tty_struct */
  do_tty_write
  /* 调用tty_struct::tty_ldisc::tty_ldisc_ops::write */
    n_tty_write
    /* 对于类型N_TTY的线路规程, write为n_tty_write */
      process_echoes
      /* 处理所有待处理的echoed字符 */
        __process_echoes
        /* 写入待处理的echo字符 */
          tty_write_room
          /* 调用tty_struct::tty_operations::write_room */
          echo_buf
          tty_put_char
          /* 
           * 调用tty_struct::tty_operations::put_char
           * 或调用tty_struct::tty_operations::write
           */
          do_output_char
          /* 调用tty_struct::tty_operations::write */
        tty_struct::tty_operations::flush_chars
        /* 对于uart为uart_flush_buffer */
      process_output_block
      process_output
      tty_struct::tty_operations::flush_chars
      tty_struct::tty_operations::write
/* 对于uart为uart_write */

uart_write分析如下

uart_write
  uart_state::circ_buf
  /* 获取环形缓冲区 */
  memcpy
  /* 将待写数据拷贝至环形缓冲区 */
  __uart_start
  uart_port::uart_ops::wake_peer
  uart_port::uart_ops::start_tx

5. 读取

read会调用tty_read, 其分析如下:

tty_read
  tty_struct::tty_ldisc::tty_ldisc_ops::read
  /* 对于类型N_TTY的线路规程, 为n_tty_read */
    n_tty_read
      tty_buffer_flush_work
        flush_work
          flush_to_ldisc
          /* 将数据从tty缓冲区刷新到线路规程 */
            receive_buf
              tty_ldisc_receive_buf
              /* 调用tty_struct::tty_ldisc::tty_ldisc_ops::receive_buf2 */
                n_tty_receive_buf2
                  n_tty_receive_buf_common
                    __receive_buf
                      n_tty_receive_buf_real_raw
      canon_copy_from_read_buf
      /* ICANON ON */
      copy_from_read_buf
      /* ICANON OFF */
        copy_to_user
        /* 将数据拷贝至用户空间 */