/dev/tty, /dev/console and /dev/ttyN

/dev/tty and /dev/console是两个虚拟设备,当 tty_open的时候才根据具体的情况,对应到具体的设备。

控制终端(/dev/tty)

如 果当前进程有控制终端(Controlling Terminal)的话,那么/dev/tty就是当前进程的控制终端的设备特殊文件。
可以使用命令”ps –ax”来查看进程与哪个控制终端相连。对于你登录的shell,/dev/tty就是你使用的终端,设备号是(5,0)。
使用命令”tty”可以查看它 具体对应哪个实际终端设备。/dev/tty有些类似于到实际所使用终端设备的一个联接。

/dev/tty代表当前tty设备,在当前的终端中输入 echo “hello” > /dev/tty ,都会直接显示在当前的终端中。
root@android:/dev # busybox tty
/dev/console
root@android:/dev # echo "5667" > /dev/console
5667
root@android:/dev # echo "5667" > /dev/tty                                     
5667

127|root@android:/ # busybox tty
/dev/pts/0
root@android:/ # echo "2222" > /dev/pts/0
2222
root@android:/ # echo "55555" > /dev/tty                                       
55555
root@android:/ # echo "55555" > /dev/pts/0                                     

55555

从以下的 open 函数看出,可以通过 /dev/tty 访问当前进程对应的tty

tty_init -> cdev_init(&tty_cdev, &tty_fops);

               register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty");

               device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty");

tty_open -> tty_open_current_tty

/**
 *    tty_open_current_tty - get tty of current task for open
 *    @device: device number
 *    @filp: file pointer to tty
 *    @return: tty of the current task iff @device is /dev/tty
 *
 *    We cannot return driver and index like for the other nodes because
 *    devpts will not work then. It expects inodes to be from devpts FS.
 */
static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp)
{
    struct tty_struct *tty;
    /*这里必须是MKDEV(TTYAUX_MAJOR, 0),否则返回,就是说失败*/
    if (device != MKDEV(TTYAUX_MAJOR, 0))
        return NULL;

    tty = get_current_tty();
    if (!tty)
        return ERR_PTR(-ENXIO);

    filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
    /* noctty = 1; */
    tty_kref_put(tty);
    /* FIXME: we put a reference and return a TTY! */
    return tty;
}

怎样从current进程宏 得到tty 的那?

crash> task_struct | grep signal
    struct signal_struct *signal;
crash> signal_struct | grep tty
    struct tty_struct *tty;
struct tty_struct *get_current_tty(void)
{
    struct tty_struct *tty;
    unsigned long flags;

    spin_lock_irqsave(&current->sighand->siglock, flags);
    tty = tty_kref_get(current->signal->tty);
    spin_unlock_irqrestore(&current->sighand->siglock, flags);
    return tty;
}

Q:/dev/console 是什么?


A:/dev/console即控制台,是与操作系统交互的设备,系统将一些信息直接输出到控制台上。目前只有在单用户模式下,才允许用户登录控制台

控制台设备/dev/console是一个接受系统信息并在单用户模式下允许登录的设备。从Linux 2.1.71开始,/dev/console由内核管理而以前的版本是一个到/dev/tty0、一个特定的虚拟控制台(如/dev/tty1)或者一个串口主(tty*,非cu*)设备动态链接,这些依赖系统配置。


从输入参数开始:console=ttyS0,115200

/*
 * Set up a list of consoles.  Called from init/main.c
 */
static int __init console_setup(char *str)
{
    char buf[sizeof(console_cmdline[0].name) + 4]; /* 4 for index */
    char *s, *options, *brl_options = NULL;
    int idx;

    /*
     * Decode str into name, index, options.
     */
    if (str[0] >= '0' && str[0] <= '9') {
        strcpy(buf, "ttyS");
        strncpy(buf + 4, str, sizeof(buf) - 5);
    } else {
        strncpy(buf, str, sizeof(buf) - 1);
    }
    buf[sizeof(buf) - 1] = 0;
    if ((options = strchr(str, ',')) != NULL)
        *(options++) = 0;

    for (s = buf; *s; s++)
        if ((*s >= '0' && *s <= '9') || *s == ',')
            break;
    idx = simple_strtoul(s, NULL, 10);
    *s = 0;

    __add_preferred_console(buf, idx, options, brl_options);
    console_set_on_cmdline = 1;
    return 1;
}
__setup("console=", console_setup);

有关/dev/console的使用?

tty_open -> tty_lookup_driver ->
    case MKDEV(TTYAUX_MAJOR, 1): {
        struct tty_driver *console_driver = console_device(index);
        if (console_driver) {
            driver = tty_driver_kref_get(console_driver);
            if (driver) {
                /* Don't let /dev/console block */
                filp->f_flags |= O_NONBLOCK;
                *noctty = 1;
                break;
            }
        }
注意这里的 tty_driver是从内核的console得到的?不是 通过tty_register_driver注册的tty_driver的方法。
对/dev/console的操作就是对 相应console设备的操作。

有关/dev/tty1,2,...,63
  1. 虚拟控制台是在系统视频监视器全屏的显示终端。

  2. 虚拟控制台设备名为/dev/tty#,编号开始于/dev/tty1。

  3. /dev/tty0是当前虚拟控制台。

  4. /dev/tty0在那些帧缓冲设备(/dev/fb*)不适用的构架下可以被用来访问系统显卡。

  5. 而/dev/console并不用于此目的。


tty_init -> vty_init(&console_fops);


int __init vty_init(const struct file_operations *console_fops)
{
    1. create the tty0
    cdev_init(&vc0_cdev, console_fops);
    if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||
        register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0)
        panic("Couldn't register /dev/tty0 driver\n");
    tty0dev = device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0");

    2. create the tty1,2,..., MAX_NR_CONSOLES
    console_driver = alloc_tty_driver(MAX_NR_CONSOLES);
    if (!console_driver)
        panic("Couldn't allocate console driver\n");

    console_driver->name = "tty";
    console_driver->name_base = 1;
    console_driver->major = TTY_MAJOR;
    console_driver->minor_start = 1;
    console_driver->type = TTY_DRIVER_TYPE_CONSOLE;
    console_driver->init_termios = tty_std_termios;
    if (default_utf8)
        console_driver->init_termios.c_iflag |= IUTF8;
    console_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_RESET_TERMIOS;
    tty_set_operations(console_driver, &con_ops);
    if (tty_register_driver(console_driver))
        panic("Couldn't register console driver\n");
    return 0;
}

static const struct tty_operations con_ops = {
    .open = con_open,
    .close = con_close,
    .write = con_write,
    .write_room = con_write_room,
    .put_char = con_put_char,
    .flush_chars = con_flush_chars,
    .chars_in_buffer = con_chars_in_buffer,
    .ioctl = vt_ioctl,

    .stop = con_stop,
    .start = con_start,
    .throttle = con_throttle,
    .unthrottle = con_unthrottle,
    .resize = vt_resize,
    .shutdown = con_shutdown
};

对ttyN的操作变成来哦对vido memory的操作


/*
 * Allocate the console screen memory.
 */
static int con_open(struct tty_struct *tty, struct file *filp)
{
    unsigned int currcons = tty->index;
    int ret = 0;

    console_lock();
    if (tty->driver_data == NULL) {
        ret = vc_allocate(currcons);
        if (ret == 0) {
            struct vc_data *vc = vc_cons[currcons].d;

            /* Still being freed */
            if (vc->port.tty) {
                console_unlock();
                return -ERESTARTSYS;
            }
            tty->driver_data = vc;
            vc->port.tty = tty;

            if (!tty->winsize.ws_row && !tty->winsize.ws_col) {
                tty->winsize.ws_row = vc_cons[currcons].d->vc_rows;
                tty->winsize.ws_col = vc_cons[currcons].d->vc_cols;
            }
            if (vc->vc_utf)
                tty->termios->c_iflag |= IUTF8;
            else
                tty->termios->c_iflag &= ~IUTF8;
            console_unlock();
            return ret;
        }
    }
    console_unlock();
    return ret;
}


具体到ttyN怎么使用,还是不清楚,看上去是和进程相关

  427 tty1     Ss+    0:00 /sbin/getty -8 38400 tty1
  881 tty4     Ss+    0:00 /sbin/getty -8 38400 tty4
  889 tty5     Ss+    0:00 /sbin/getty -8 38400 tty5
  906 tty2     Ss+    0:00 /sbin/getty -8 38400 tty2
  907 tty3     Ss+    0:00 /sbin/getty -8 38400 tty3
  909 tty6     Ss+    0:00 /sbin/getty -8 38400 tty6
25577 tty7     Rs+  1234:42 /usr/bin/X :0 -auth /var/run/lightdm/root/:0 -nolisten tcp vt7 -novtswitch



printk的实现细节

/**
 * printk - print a kernel message
 * @fmt: format string
 *
 * This is printk().  It can be called from any context.  We want it to work.
 *
 * We try to grab the console_lock.  If we succeed, it's easy - we log the output and
 * call the console drivers.  If we fail to get the semaphore we place the output
 * into the log buffer and return.  The current holder of the console_sem will
 * notice the new output in console_unlock(); and will send it to the
 * consoles before releasing the lock.
 *
 * One effect of this deferred printing is that code which calls printk() and
 * then changes console_loglevel may break. This is because console_loglevel
 * is inspected when the actual printing occurs.
 *
 * See also:
 * printf(3)
 *
 * See the vsnprintf() documentation for format string extensions over C99.
 */

asmlinkage int printk(const char *fmt, ...)
{
    va_list args;
    int r;

    va_start(args, fmt);
    r = vprintk(fmt, args);
    va_end(args);

    return r;
}

vprink

asmlinkage int vprintk(const char *fmt, va_list args)
{
    int printed_len = 0;
    int current_log_level = default_message_loglevel;
    unsigned long flags;
    int this_cpu;
    char *p;
    size_t plen;
    char special;

    boot_delay_msec();
    printk_delay();

    /* This stops the holder of console_sem just where we want him */
    local_irq_save(flags);
    this_cpu = smp_processor_id();

    /*
     * Ouch, printk recursed into itself!
     */
    if (unlikely(printk_cpu == this_cpu)) {
        /*
         * If a crash is occurring during printk() on this CPU,
         * then try to get the crash message out but make sure
         * we can't deadlock. Otherwise just return to avoid the
         * recursion and return - but flag the recursion so that
         * it can be printed at the next appropriate moment:
         */
        if (!oops_in_progress && !lockdep_recursing(current)) {
            recursion_bug = 1;
            goto out_restore_irqs;
        }
        zap_locks();
    }

    lockdep_off();
    raw_spin_lock(&logbuf_lock);
    printk_cpu = this_cpu;

    if (recursion_bug) {
        recursion_bug = 0;
        strcpy(printk_buf, recursion_bug_msg);
        printed_len = strlen(recursion_bug_msg);
    }
    /* Emit the output into the temporary buffer */
    printed_len += vscnprintf(printk_buf + printed_len,
                  sizeof(printk_buf) - printed_len, fmt, args);

    p = printk_buf;

    /* Read log level and handle special printk prefix */
    plen = log_prefix(p, &current_log_level, &special);
    if (plen) {
        p += plen;

        switch (special) {
        case 'c': /* Strip <c> KERN_CONT, continue line */
            plen = 0;
            break;
        case 'd': /* Strip <d> KERN_DEFAULT, start new line */
            plen = 0;
        default:
            if (!new_text_line) {
                emit_log_char('\n');
                new_text_line = 1;
            }
        }
    }

    /*
     * Copy the output into log_buf. If the caller didn't provide
     * the appropriate log prefix, we insert them here
     */
    for (; *p; p++) {
        if (new_text_line) {
            new_text_line = 0;

            if (plen) {
                /* Copy original log prefix */
                int i;

                for (i = 0; i < plen; i++)
                    emit_log_char(printk_buf[i]);
                printed_len += plen;
            } else {
                /* Add log prefix */
                emit_log_char('<');
                emit_log_char(current_log_level + '0');
                emit_log_char('>');
                printed_len += 3;
            }

            if (printk_time) {
                /* Add the current time stamp */
                char tbuf[50], *tp;
                unsigned tlen;
                unsigned long 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;
    }

    /*
     * Try to acquire and then immediately release the
     * console semaphore. The release will do all the
     * actual magic (print out buffers, wake up klogd,
     * etc).
     *
     * The console_trylock_for_printk() function
     * will release 'logbuf_lock' regardless of whether it
     * actually gets the semaphore or not.
     */
    if (console_trylock_for_printk(this_cpu))
        console_unlock();

    lockdep_on();
out_restore_irqs:
    local_irq_restore(flags);

    return printed_len;
}


/**

 * console_unlock - unlock the console system

 *
 * Releases the console_lock which the caller holds on the console system
 * and the console driver list.
 *
 * While the console_lock was held, console output may have been buffered
 * by printk().  If this is the case, console_unlock(); emits
 * the output prior to releasing the lock.
 *
 * If there is output waiting for klogd, we wake it up.
 *
 * console_unlock(); may be called from any context.
 */
void console_unlock(void)
{
    unsigned long flags;
    unsigned _con_start, _log_end;
    unsigned wake_klogd = 0, retry = 0;

    if (console_suspended) {
        up(&console_sem);
        return;
    }

    console_may_schedule = 0;

again:
    for ( ; ; ) {
        raw_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 */
        raw_spin_unlock(&logbuf_lock);
        stop_critical_timings();    /* don't trace print latency */
        call_console_drivers(_con_start, _log_end);
        start_critical_timings();
        local_irq_restore(flags);
    }
    console_locked = 0;

    /* Release the exclusive_console once it is used */
    if (unlikely(exclusive_console))
        exclusive_console = NULL;

    raw_spin_unlock(&logbuf_lock);

    up(&console_sem);

    /*
     * Someone could have filled up the buffer again, so re-check if there's
     * something to flush. In case we cannot trylock the console_sem again,
     * there's a new owner and the console_unlock() from them will do the
     * flush, no worries.
     */
    raw_spin_lock(&logbuf_lock);
    if (con_start != log_end)
        retry = 1;
    raw_spin_unlock_irqrestore(&logbuf_lock, flags);

    if (retry && console_trylock())
        goto again;

    if (wake_klogd)
        wake_up_klogd();
}


/*

 * Call the console drivers, asking them to write out

 * log_buf[start] to log_buf[end - 1].
 * The console_lock must be held.
 */
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)) {
            /* strip log prefix */
            cur_index += log_prefix(&LOG_BUF(cur_index), &msg_level, NULL);
            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_console_drivers(start_print, end, msg_level);
}


_call_console_drivers

/*
 * Write out chars from start to end - 1 inclusive
 */
static void _call_console_drivers(unsigned start,
                unsigned end, int msg_log_level)
{
    trace_console(&LOG_BUF(0), start, end, log_buf_len);

    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);
        }
    }
}

__call_console_drivers

/*
 * Call the console drivers on a range of log_buf
 */
static void __call_console_drivers(unsigned start, unsigned end)
{
    struct console *con;

    for_each_console(con) {
        if (exclusive_console && con != exclusive_console)
            continue;
        if ((con->flags & CON_ENABLED) && con->write &&
                (cpu_online(smp_processor_id()) ||
                (con->flags & CON_ANYTIME)))
            con->write(con, &LOG_BUF(start), end - start);
    }
}


serial8250_console_init -> register_console

static int __init serial8250_console_init(void)
{
    if (nr_uarts > UART_NR)
        nr_uarts = UART_NR;

    serial8250_isa_init_ports();
    register_console(&serial8250_console);
    return 0;crash> console_drivers
console_drivers = $1 = (struct console *) 0xc0704908 <serial8250_console>
crash> serial8250_console
serial8250_console = $2 = {
  name = "ttyS\000\000\000\000\000\000\000\000\000\000\000",
  write = 0xc01f47dc <serial8250_console_write>,
  read = 0x0,
  device = 0xc01ef074 <uart_console_device>,
  unblank = 0x0,
  setup = 0xc06bd910 <serial8250_console_setup>,
  early_setup = 0xc01f28e4 <serial8250_console_early_setup>,
  flags = 23,
  index = 0,
  cflag = 0,
  data = 0xc0704878 <serial8250_reg>,
  next = 0x0
}
}

怎么看不到read 函数?

难道读写都在函数 serial8250_console_write中?不是。

drivers\char\tty_io.c:

tty_init:

{

    cdev_init(&console_cdev, &console_fops);
    if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
        register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
        panic("Couldn't register /dev/console driver\n");
    consdev = device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL,
                  "console");

}

static const struct file_operations console_fops = {
    .llseek        = no_llseek,
    .read        = tty_read,
    .write        = redirected_tty_write,
    .poll        = tty_poll,
    .unlocked_ioctl    = tty_ioctl,
    .compat_ioctl    = tty_compat_ioctl,
    .open        = tty_open,
    .release    = tty_release,
    .fasync        = tty_fasync,
};

这里可以看出/dev/console本身是一个tty设备,他上层的读函数是 tty_read,他的tty_driver中没有实现读操作,读数据是通过

读中断的处理函数 实现的。




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值