/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(¤t->sighand->siglock, flags);
tty = tty_kref_get(current->signal->tty);
spin_unlock_irqrestore(¤t->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
-
虚拟控制台是在系统视频监视器上全屏的显示终端。
-
虚拟控制台设备名为/dev/tty#,编号开始于/dev/tty1。
-
/dev/tty0是当前虚拟控制台。
-
/dev/tty0在那些帧缓冲设备(/dev/fb*)不适用的构架下可以被用来访问系统显卡。
-
而/dev/console并不用于此目的。
虚拟控制台是在系统视频监视器上全屏的显示终端。
虚拟控制台设备名为/dev/tty#,编号开始于/dev/tty1。
/dev/tty0是当前虚拟控制台。
/dev/tty0在那些帧缓冲设备(/dev/fb*)不适用的构架下可以被用来访问系统显卡。
而/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, ¤t_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中没有实现读操作,读数据是通过
读中断的处理函数 实现的。