linux类系统相较于windos类系统用“魔幻”也不为过,神奇的控制台终端如ctrl+alt+F1至F6、UI桌面打开终端、远程ssh登录等等,在神奇的命令行协助下总能随时随地完成手中的工作。
  当然,这些功能虽不算复杂,但庞大的架构及繁多的代码让人有一种深不测的感觉。经过一段时间对终端相关代码的分析对它们有了初步的了解。它们采用较为一致的思路方式实现,代码难度也不算太高,具备字符驱动相关的知识加上足够的耐心便可以解开这个“庞然大物”。
  linux终端子系统主要围绕着外设(输入设备)到内核的输入事件处理及转发,到达我们目前看到的效果。
  大致思路为:外设 -> input事件子系统 -> tty子系统 -> 输出/输入对应的终端种类,如console、pty等。
  终端类型可以分为:tty子系统、tty设备、console设备、pty(伪终端)设备等等,在pty这一层又做了不同的处理对应/dev/pts/ptmx->/dev/ptmx->/dev/pts/*等等,由于这部分内容过于繁多,这里先简单描述一小部分执行逻辑,之后应该还会有相关文章继续向后扩展内容。

目录

1. tty

1.1 tty_class_init

1.2 tty子系统注册

2. console

2.1 console注册

2.2 con_init

3. 源码结构

4. 部分结构定义

5. 扩展函数/变量

1. tty

1.1 tty_class_init

tty子系统首先创建tty class,绑定设备号(动态生成),由class对象tty_class关联/dev/目录下的tty相关访问文件,如/dev/tty0、/dev/tty1

通常情况下,驱动通过class名称与其建立关系,如pci_driver的.name字段指定class名称

static int __init tty_class_init(void)
{tty_class = class_create(THIS_MODULE, "tty"); // 创建tty class指向__this_module(模块所有者)
||
\/
——————————————————————————————————————————————————————————————————————
define class_create(owner, name)               \
({                                              \static struct lock_class_key __key;     \__class_create(owner, name, &__key);    \
})#ifdef MODULE
extern struct module __this_module;
#define THIS_MODULE (&__this_module)
#else
#define THIS_MODULE ((struct module *)0)
#endif
——————————————————————————————————————————————————————————————————————if (IS_ERR(tty_class))return PTR_ERR(tty_class);tty_class->devnode = tty_devnode; // 设备号return 0;
}postcore_initcall(tty_class_init);||
\/
ic char *tty_devnode(struct device *dev, umode_t *mode)
{if (!mode)return NULL;if (dev->devt == MKDEV(TTYAUX_MAJOR, 0) ||dev->devt == MKDEV(TTYAUX_MAJOR, 2))*mode = 0666;return NULL;
}

class

1.2 tty子系统注册

tty子系统通过tty_init函数完成注册过程,它在字符设备初始化函数chr_dev_init中调用,tty子系统实际上也属于字符设备

tty子系统通过tty_fops结构中的函数完成应用层的访问,如应用程序调用open(“/dev/tty”…)函数

tty_sysctl_init(); // 注册tty子系统标签(配置参数)
||
\/
register_sysctl_table(tty_root_table);

tty_root_table

cdev_init(&tty_cdev, &tty_fops); // 初始化tty_cdev设备,关联tty_fops结构if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0) // 注册tty_cdev设备panic("Couldn't register /dev/tty driver\n");device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL, "tty"); // 创建tty_cdev设备,指向tty_class,通过open等函数访问/dev/tty
...return 0;
} fs_initcall(chr_dev_init);

tty_fops

2. console

2.1 console注册

console紧跟着 tty子系统之后注册,它具有自己的操作结构console_fops,它在内核中的形式与tty子系统相似,基于tty_class创建consdev设备
  vty_init函数注册vc0_cdev设备(/dev/tty0),通过创建vcs、vcsu、vcsa相关设备,完成向虚拟图形界面的数据输出等工作。vty_init函数末尾可以看到键盘驱动注册函数(通常情况下,不同的按键厂商还是会做自己的驱动,至少在设备ID、厂商ID这部分是这样)。

cdev_init(&console_cdev, &console_fops); // 初始化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) // 注册console_cdev设备panic("Couldn't register /dev/console driver\n");

console_fops

consdev = device_create_with_groups(tty_class, NULL,MKDEV(TTYAUX_MAJOR, 1), NULL,cons_dev_groups, "console");
// 创建consdev设备,指向tty_class,通过open等函数访问/dev/console if (IS_ERR(consdev))consdev = NULL;

cons_dev_groups

#ifdef CONFIG_VTvty_init(&console_fops);
#endifreturn 0;
}
||
\/
int __init vty_init(const struct file_operations *console_fops)
{cdev_init(&vc0_cdev, console_fops); // 初始化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) // 注册vc0_cdev设备panic("Couldn't register /dev/tty0 driver\n");tty0dev = device_create_with_groups(tty_class, NULL,MKDEV(TTY_MAJOR, 0), NULL,vt_dev_groups, "tty0");//注册tty0dev设备,指向tty_class,通过open等函数访问/dev/tty0 if (IS_ERR(tty0dev))tty0dev = NULL;vcs_init(); // 注册、创建vcs、vcsu、vcsa设备

vcs_init

console_driver = tty_alloc_driver(MAX_NR_CONSOLES, TTY_DRIVER_REAL_RAW |TTY_DRIVER_RESET_TERMIOS); // 分配cosole驱动if (IS_ERR(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;

tty_alloc_driver

if (default_utf8)console_driver->init_termios.c_iflag |= IUTF8;tty_set_operations(console_driver, &con_ops); // console_driver关联con_ops结构if (tty_register_driver(console_driver)) // 注册tty设备,指定tty_class,这里包括tty的多个种类,如console、ptypanic("Couldn't register console driver\n");kbd_init(); // 注册键盘驱动console_map_init(); // 设置默认unicode映射
#ifdef CONFIG_MDA_CONSOLEmda_console_init();
#endifreturn 0;
}

con_ops
tty_register_driver
kbd_init

2.2 con_init

虚拟控制台相关的初始化,如控制台的回调、控制台的屏幕设置、vc_SAK组合按键监控触发的事件、虚拟屏幕缓存分配等等,最后注册到通知链,表示控制台已分配完成

static int __init con_init(void)
{const char *display_desc = NULL;struct vc_data *vc;unsigned int currcons = 0, i;console_lock();if (!conswitchp)conswitchp = &dummy_con; // 虚拟控制台的控制台“开关”结构 (consw-控制台的回调)display_desc = conswitchp->con_startup(); // "dummy device"if (!display_desc) {    fg_console = 0;console_unlock();return 0;}

dummy_con

for (i = 0; i < MAX_NR_CON_DRIVER; i++) {struct con_driver *con_driver = &registered_con_driver[i];if (con_driver->con == NULL) { // 虚拟控制台赋值con_driver->con = conswitchp;con_driver->desc = display_desc; // "dummy device"con_driver->flag = CON_DRIVER_FLAG_INIT;con_driver->first = 0;con_driver->last = MAX_NR_CONSOLES - 1;break;}}for (i = 0; i < MAX_NR_CONSOLES; i++)con_driver_map[i] = conswitchp;if (blankinterval) {blank_state = blank_normal_wait;mod_timer(&console_timer, jiffies + (blankinterval * HZ)); // 虚拟控制台(屏幕,ctl+alt+F2...F6这种屏幕,而ctl+alt+F1属于控制台启动的类Xwindow这种模拟画面)刷新时间}for (currcons = 0; currcons < MIN_NR_CONSOLES; currcons++) {vc_cons[currcons].d = vc = kzalloc(sizeof(struct vc_data), GFP_NOWAIT); // 虚拟控制台分配INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK); // 初始化工作队列,vc_SAK属于组合按键监控触发的事件,如alt+k+Prtscreentty_port_init(&vc->port); // 初始化tty端口及tty_bufhead等结构,用于翻转缓冲区刷新到线路规程visual_init(vc, currcons, 1); // 初始化虚拟控制台的一些参数/* Assuming vc->vc_{cols,rows,screenbuf_size} are sane here. */vc->vc_screenbuf = kzalloc(vc->vc_screenbuf_size, GFP_NOWAIT); // 虚拟屏幕缓存分配vc_init(vc, vc->vc_rows, vc->vc_cols,currcons || !vc->vc_sw->con_save_screen); // 虚拟控制台屏幕的模式、范围、颜色等设置}

tty_port_init
visual_init
vc_init

vcs_make_sysfs(currcons); // 创建当前的vcs*、vcsu*、vcsa*设备...atomic_notifier_call_chain(&vt_notifier_list, VT_ALLOCATE, &param); // 注册到通知链,表示控制台已分配return 0;
err_free:visual_deinit(vc);kfree(vc);vc_cons[currcons].d = NULL;return err;
}

3. 源码结构

tty子系统标签

tatic struct ctl_table tty_root_table[] = {{.procname       = "dev",.mode           = 0555,.child          = tty_dir_table, // 子级标签}, { }
};

tty子系统子级标签

static struct ctl_table tty_dir_table[] = {{.procname       = "tty",.mode           = 0555,.child          = tty_table, // 子级标签},      { }
};

tty子系统子级标签 ,通过/proc/sys/dev/tty/ldisc_autoload来传递数字

static struct ctl_table tty_table[] = {{.procname       = "ldisc_autoload",.data           = &tty_ldisc_autoload,.maxlen         = sizeof(tty_ldisc_autoload),.mode           = 0644,.proc_handler   = proc_dointvec,.extra1         = SYSCTL_ZERO,.extra2         = SYSCTL_ONE,},{ }
};

tty_fops 文件操作接口

static const struct file_operations tty_fops = {.llseek         = no_llseek,.read_iter      = tty_read,.write_iter     = tty_write,.splice_read    = generic_file_splice_read,.splice_write   = iter_file_splice_write,.poll           = tty_poll,.unlocked_ioctl = tty_ioctl,.compat_ioctl   = tty_compat_ioctl,.open           = tty_open,.release        = tty_release,.fasync         = tty_fasync,.show_fdinfo    = tty_show_fdinfo,
};

console_fops 文件操作接口

static const struct file_operations console_fops = {.llseek     = no_llseek,.read_iter = tty_read,.write_iter = redirected_tty_write,.splice_read    = generic_file_splice_read,.splice_write   = iter_file_splice_write,.poll     = tty_poll,.unlocked_ioctl = tty_ioctl,.compat_ioctl  = tty_compat_ioctl,.open       = tty_open,.release    = tty_release,.fasync      = tty_fasync,
};

vcs_fops 文件操作接口

static const struct file_operations vcs_fops = {.llseek         = vcs_lseek,.read           = vcs_read,.write          = vcs_write,.poll           = vcs_poll,.fasync         = vcs_fasync,.open           = vcs_open,.release        = vcs_release,
};

con_ops 文件操作接口

static const struct tty_operations con_ops = {.install = con_install,.open = con_open,.close = con_close,.write = con_write,.write_room = con_write_room,.put_char = con_put_char,.flush_chars = con_flush_chars,.ioctl = vt_ioctl,
#ifdef CONFIG_COMPAT.compat_ioctl = vt_compat_ioctl,
#endif.stop = con_stop,.start = con_start,.throttle = con_throttle,.unthrottle = con_unthrottle,.resize = vt_resize,.shutdown = con_shutdown,.cleanup = con_cleanup,
};

dummy_con 虚拟控制台的控制台“开关”结构

const struct consw dummy_con = {.owner =                THIS_MODULE,.con_startup =  dummycon_startup,.con_init =             dummycon_init,.con_deinit =   dummycon_deinit,.con_clear =    dummycon_clear,.con_putc =             dummycon_putc,.con_putcs =    dummycon_putcs,.con_cursor =   dummycon_cursor,.con_scroll =   dummycon_scroll,.con_switch =   dummycon_switch,.con_blank =    dummycon_blank,
};
EXPORT_SYMBOL_GPL(dummy_con);

tty_port_default_client_ops

const struct tty_port_client_operations tty_port_default_client_ops = {.receive_buf = tty_port_default_receive_buf,.lookahead_buf = tty_port_default_lookahead_buf,.write_wakeup = tty_port_default_wakeup,
};
EXPORT_SYMBOL_GPL(tty_port_default_client_ops);

4. 部分结构定义

struct class

* struct class——设备类* @name:类名。* @owner:模块所有者。* @class_groups:该类的默认属性。* @dev_groups:属于该类的设备的默认属性。* @dev_kobj:表示这个类并将其链接到层次结构中的kobject。* @dev_uevent:当设备被添加或从该类中移除时调用*很少其他东西生成uevent来添加环境*变量。* @devnode:回调提供devtmpfs。* @class_release:调用来释放这个类。* @dev_release:被调用释放设备。* @shutdown_pre:在驱动程序关闭前的关闭时间调用。* @ns_type:回调使sysfs可以确定名称空间。* @namespace:设备的命名空间属于该类。* @get_ownership:允许类指定sysfs目录的uid/gid*表示属于该类的设备。通常与*设备的名称空间。* @pm:该类的默认设备电源管理操作。* @p:驱动核心的私有数据,不是别人*驱动核心可以触摸这个。**类是设备的高级视图,它抽象出了底层*实现细节。驱动程序可能看到的是SCSI磁盘或ATA磁盘,但是,*在类级别,它们都是简单的磁盘。类允许用户空间*根据设备的功能,而不是它们的性能来使用它们*连接或他们如何工作。struct class {const char      *name;struct module     *owner;const struct attribute_group **class_groups;const struct attribute_group **dev_groups;struct kobject         *dev_kobj;int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);char *(*devnode)(struct device *dev, umode_t *mode);void (*class_release)(struct class *class);void (*dev_release)(struct device *dev);int (*shutdown_pre)(struct device *dev);const struct kobj_ns_type_operations *ns_type;const void *(*namespace)(struct device *dev);void (*get_ownership)(struct device *dev, kuid_t *uid, kgid_t *gid);const struct dev_pm_ops *pm;struct subsys_private *p;
};

5. 扩展函数/变量

cons_dev_groups 经过转换为 attribute_group结构的静态对象

ATTRIBUTE_GROUPS(cons_dev);
||
\/
#define ATTRIBUTE_GROUPS(_name)                                 \
static const struct attribute_group _name##_group = {           \.attrs = _name##_attrs,                                 \
};                                                              \
__ATTRIBUTE_GROUPS(_name)
||
\/
define __ATTRIBUTE_GROUPS(_name)                               \
static const struct attribute_group *_name##_groups[] = {       \&_name##_group,                                         \NULL,                                                   \
}转换成结构定义为:
static const struct attribute_group cons_dev_group = {           .attrs = cons_dev_attrs,
};                                                              static const struct attribute_group *cons_dev_groups[] = {       &cons_dev_group,                                         NULL,
}

vcs_init 注册、创建vcs、vcsu、vcsa设备

int __init vcs_init(void)
{                       unsigned int i;if (register_chrdev(VCS_MAJOR, "vcs", &vcs_fops))panic("unable to get major %d for vcs device", VCS_MAJOR); // 注册vcs设备,关联vcs_fopsvc_class = class_create(THIS_MODULE, "vc"); // 创建vc class,指向__this_module(模块所有者)device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 0), NULL, "vcs"); // 创建vcs设备,指向vc_class...device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 64), NULL, "vcsu"); // 创建vcsu设备...device_create(vc_class, NULL, MKDEV(VCS_MAJOR, 128), NULL, "vcsa"); // 创建vcsa设备...for (i = 0; i < MIN_NR_CONSOLES; i++)// define MIN_NR_CONSOLES 1vcs_make_sysfs(i); // 创建多个vcs*、vcsu*、vcsa*设备...return 0;
}
||
\/
void vcs_make_sysfs(int index)
{device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 1), NULL,"vcs%u", index + 1);device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 65), NULL,"vcsu%u", index + 1);device_create(vc_class, NULL, MKDEV(VCS_MAJOR, index + 129), NULL,"vcsa%u", index + 1);
}

vcs_fops

tty_alloc_driver 分配tty驱动

struct tty_driver *__tty_alloc_driver(unsigned int lines, struct module *owner,unsigned long flags)
{struct tty_driver *driver;unsigned int cdevs = 1;int err;if (!lines || (flags & TTY_DRIVER_UNNUMBERED_NODE && lines > 1))return ERR_PTR(-EINVAL);driver = kzalloc(sizeof(*driver), GFP_KERNEL);if (!driver)return ERR_PTR(-ENOMEM);kref_init(&driver->kref);driver->num = lines;driver->owner = owner;driver->flags = flags;/* TTY_DRIVER_DEVPTS_MEM  0x0010** 不要使用标准的数组(&tty_driver.ttys和&tty_driver.termios)* 而是使用通过devpts文件系统输入的动态内存* 这只适用于PTY驱动程序**/if (!(flags & TTY_DRIVER_DEVPTS_MEM)) {driver->ttys = kcalloc(lines, sizeof(*driver->ttys),GFP_KERNEL);driver->termios = kcalloc(lines, sizeof(*driver->termios),GFP_KERNEL);if (!driver->ttys || !driver->termios) {err = -ENOMEM;goto err_free_all;}}/* TTY_DRIVER_DYNAMIC_ALLOC  0x0040** 不要为驱动程序(&tty_driver.ports)分配每行需要的结构* 因为这会浪费内存* 这只适用于PTY驱动程序**/if (!(flags & TTY_DRIVER_DYNAMIC_ALLOC)) {driver->ports = kcalloc(lines, sizeof(*driver->ports),GFP_KERNEL);if (!driver->ports) {err = -ENOMEM;goto err_free_all;}cdevs = lines;}driver->cdevs = kcalloc(cdevs, sizeof(*driver->cdevs), GFP_KERNEL);if (!driver->cdevs) {err = -ENOMEM;goto err_free_all;}return driver;...
}EXPORT_SYMBOL(__tty_alloc_driver);

tty_register_driver

int tty_register_driver(struct tty_driver *driver)
{int error;int i;dev_t dev;struct device *d;if (!driver->major) {error = alloc_chrdev_region(&dev, driver->minor_start,driver->num, driver->name);// 分配设备号if (!error) {driver->major = MAJOR(dev);driver->minor_start = MINOR(dev);}} else {dev = MKDEV(driver->major, driver->minor_start);error = register_chrdev_region(dev, driver->num, driver->name);// 注册设备号}if (error < 0)goto err;if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) {error = tty_cdev_add(driver, dev, 0, driver->num); // 分配、注册驱动并且关联tty_fopsif (error)goto err_unreg_char;}

tty_cdev_add

 mutex_lock(&tty_mutex);list_add(&driver->tty_drivers, &tty_drivers); // 驱动加入tty链表mutex_unlock(&tty_mutex);if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {for (i = 0; i < driver->num; i++) {d = tty_register_device(driver, i, NULL);if (IS_ERR(d)) {error = PTR_ERR(d);goto err_unreg_devs;}}}

tty_register_device

proc_tty_register_driver(driver);driver->flags |= TTY_DRIVER_INSTALLED;return 0;

tty_cdev_add

static int tty_cdev_add(struct tty_driver *driver, dev_t dev,unsigned int index, unsigned int count)
{int err;/* init here, since reused cdevs cause crashes */driver->cdevs[index] = cdev_alloc(); // 分配字符设备结构if (!driver->cdevs[index])return -ENOMEM;driver->cdevs[index]->ops = &tty_fops; // 为驱动关联tty_fops结构driver->cdevs[index]->owner = driver->owner;err = cdev_add(driver->cdevs[index], dev, count); // 注册字符设备if (err)kobject_put(&driver->cdevs[index]->kobj);return err;
}

tty_register_device 注册tty设备,指定tty_class,这里包括tty的多个种类,如console、pty

vice *tty_register_device(struct tty_driver *driver, unsigned index,struct device *device)
{return tty_register_device_attr(driver, index, device, NULL, NULL);
}
||
\/
struct device *tty_register_device_attr(struct tty_driver *driver,unsigned index, struct device *device,void *drvdata,const struct attribute_group **attr_grp)
{char name[64];dev_t devt = MKDEV(driver->major, driver->minor_start) + index;struct ktermios *tp;struct device *dev;int retval;if (index >= driver->num) {pr_err("%s: Attempt to register invalid tty line number (%d)\n",driver->name, index);return ERR_PTR(-EINVAL);}/*  tty设备类型*  #define TTY_DRIVER_TYPE_SYSTEM          0x0001  内部使用*  #define TTY_DRIVER_TYPE_CONSOLE         0x0002  控制台*  #define TTY_DRIVER_TYPE_SERIAL          0x0003  串口*  #define TTY_DRIVER_TYPE_PTY             0x0004  PTY(伪终端)*  #define TTY_DRIVER_TYPE_SCC             0x0005  /* scc driver */*  #define TTY_DRIVER_TYPE_SYSCONS         0x0006 内部使用*/if (driver->type == TTY_DRIVER_TYPE_PTY)pty_line_name(driver, index, name); // pty通用名称,PTY_TYPE_SLAVE类型指定为"tty",其他类型为nameelsetty_line_name(driver, index, name); // 包含TTY_DRIVER_UNNUMBERED_NODE类型创建名称如"/dev/ttyprintk",否则如"/dev/ttyprintk0..."dev = kzalloc(sizeof(*dev), GFP_KERNEL);if (!dev)return ERR_PTR(-ENOMEM);dev->devt = devt;dev->class = tty_class; // 设置指定tty_classdev->parent = device;dev->release = tty_device_create_release;dev_set_name(dev, "%s", name);dev->groups = attr_grp;dev_set_drvdata(dev, drvdata);dev_set_uevent_suppress(dev, 1); // dev->kobj.uevent_suppress=1retval = device_register(dev); // 注册设备...
}

kbd_init 注册键盘驱动

int __init kbd_init(void)
{int i;int error;for (i = 0; i < MAX_NR_CONSOLES; i++) {kbd_table[i].ledflagstate = kbd_defleds(); // &0x20 ? 数字锁定模式kbd_table[i].default_ledflagstate = kbd_defleds();kbd_table[i].ledmode = LED_SHOW_FLAGS; // 传统模式kbd_table[i].lockstate = KBD_DEFLOCK;kbd_table[i].slockstate = 0;kbd_table[i].modeflags = KBD_DEFMODE; // 键盘重复 | 0-meta,1-meta=带ESC的前缀kbd_table[i].kbdmode = default_utf8 ? VC_UNICODE : VC_XLATE; // Unicode模式}kbd_init_leds(); // LED触发接口error = input_register_handler(&kbd_handler); // 注册新的输入处理程序if (error)return error;tasklet_enable(&keyboard_tasklet); // 用于减小结构体tasklet_struct中字段count的值,等于0时重新相应软中断重新使能tasklet_schedule(&keyboard_tasklet); // 等待获得CPU资源并被调度执行return 0;
}

tty_port_init 初始化tty端口及tty_bufhead等结构,用于翻转缓冲区刷新到线路规程

void tty_port_init(struct tty_port *port)
{               memset(port, 0, sizeof(*port));tty_buffer_init(port); // 初始化tty_bufhead结构,初始化工作队列,用于flush_to_ldisc函数 (翻转缓冲区刷新到线路规程)init_waitqueue_head(&port->open_wait);init_waitqueue_head(&port->delta_msr_wait);mutex_init(&port->mutex);mutex_init(&port->buf_mutex);spin_lock_init(&port->lock);port->close_delay = (50 * HZ) / 100;port->closing_wait = (3000 * HZ) / 100;port->client_ops = &tty_port_default_client_ops;kref_init(&port->kref);
}
EXPORT_SYMBOL(tty_port_init);

tty_port_default_client_ops

visual_init 初始化虚拟控制台的一些参数

static void visual_init(struct vc_data *vc, int num, int init)
{/* ++Geert: vc->vc_sw->con_init determines console size */if (vc->vc_sw)module_put(vc->vc_sw->owner);vc->vc_sw = conswitchp; // 虚拟控制台的控制台“开关”结构
#ifndef VT_SINGLE_DRIVERif (con_driver_map[num])vc->vc_sw = con_driver_map[num]; // consw-控制台的回调
#endif__module_get(vc->vc_sw->owner);vc->vc_num = num; // 控制台编号/* 对于每个现有的显示器,* 我们都有一个指向该显示器上当前可见的控制台的指针,* 允许适当刷新fg_console以外的控制台。* 除非低级驱动程序提供自己的display_fg变量,* 否则我们将此变量用于“主显示”。* /vc->vc_display_fg = &master_display_fg;if (vc->uni_pagedict_loc)con_free_unimap(vc);vc->uni_pagedict_loc = &vc->uni_pagedict;vc->uni_pagedict = NULL;vc->vc_hi_font_mask = 0;vc->vc_complement_mask = 0;vc->vc_can_do_color = 0;/** #define DEFAULT_BELL_PITCH      750* #define DEFAULT_BELL_DURATION   (HZ/8)* #define DEFAULT_CURSOR_BLINK_MS 200*/vc->vc_cur_blink_ms = DEFAULT_CURSOR_BLINK_MS;vc->vc_sw->con_init(vc, init); // dummycon_init函数if (!vc->vc_complement_mask)vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;vc->vc_s_complement_mask = vc->vc_complement_mask;vc->vc_size_row = vc->vc_cols << 1;vc->vc_screenbuf_size = vc->vc_rows * vc->vc_size_row;
}

vc_init 虚拟控制台屏幕的模式、范围、颜色等设置

static void vc_init(struct vc_data *vc, unsigned int rows,unsigned int cols, int do_clear)
{int j, k ;vc->vc_cols = cols; vc->vc_rows = rows;vc->vc_size_row = cols << 1;vc->vc_screenbuf_size = vc->vc_rows * vc->vc_size_row;set_origin(vc);vc->vc_pos = vc->vc_origin;reset_vc(vc);for (j=k=0; j<16; j++) {vc->vc_palette[k++] = default_red[j] ;vc->vc_palette[k++] = default_grn[j] ;vc->vc_palette[k++] = default_blu[j] ;}vc->vc_def_color       = default_color;vc->vc_ulcolor         = default_underline_color;vc->vc_itcolor         = default_italic_color;vc->vc_halfcolor       = 0x08;   /* grey */init_waitqueue_head(&vc->paste_wait);reset_terminal(vc, do_clear);
}

linux终端设备:tty子系统相关的初始化相关推荐

  1. linux终端设备:pty设备初始化、创建过程

    上一篇<<linux终端设备:tty子系统相关的初始化>>分析了tty子系统.tty设备.console设备的初始化及创建过程.默认情况console终端的启动程序为agett ...

  2. Linux TTY子系统(1) - 了解TTY

    了解linux tty 1.TTY概述   TTY 是 Teletype 或 Teletypewriter 的缩写,原来是指电传打字机,后来这种设备逐渐键盘和显示器取代.不管是电传打字机还是键盘显示器 ...

  3. linux 终端 tty 简介

    TTY 是 Teletype 或 Teletypewriter 的缩写,原来是指电传打字机,后来这种设备逐渐键盘和显示器取代.不管是电传打字机还是键盘显示器,都是作为计算机的终端设备存在的,所以 TT ...

  4. Linux 终端(TTY)

    TTY 是 Teletype 或 Teletypewriter 的缩写,原来是指电传打字机,后来这种设备逐渐键盘和显示器取代.不管是电传打字机还是键盘显示器,都是作为计算机的终端设备存在的,所以 TT ...

  5. linux tty 教程,Linux 终端(TTY)

    TTY 是 Teletype 或 Teletypewriter 的缩写,原来是指电传打字机,后来这种设备逐渐键盘和显示器取代.不管是电传打字机还是键盘显示器,都是作为计算机的终端设备存在的,所以 TT ...

  6. 一文彻底讲清Linux tty子系统架构及编程实例

    [摘要]本文详细解读了linux系统下的tty子系统的深层次原理和架构,并参考了LDD3中的代码实例讲述了无硬件下实现一个简单的tty设备驱动模块的编写.对了解tty子系统及下部串口驱动模块的学习有较 ...

  7. linux TTY子系统 --- bug来了,稍候再整理

    Version: 0.9 Date: 2018-09-27 Nemo.Han 目录 1.学习出发点及目标 1.1 学习出发点 1.2 学习目标 2 什么是TTY 2.1 串口 2.2 伪终端 2.3 ...

  8. linux TTY子系统(2) -- 软件框架

    了解tty 子系统 1.TTY的子系统   在Linux kernel中,TTY就是各类终端(Terminal)的简称.为了简化终端的使用,以及终端驱动程序的编写,Linux kernel抽象出了TT ...

  9. linux 串口驱动 4412,⑮tiny4412 Linux驱动开发之tty子系统(UART)驱动程序

    本次说一下tty子系统的驱动编程,因为UART相关的寄存器比较多,同时,应用比较广泛,所以本次的驱动程序量也不少,而且只是完成和特定CPU相关的一部分,通用的部分本次都没有涉及到.在写驱动之前,我们先 ...

最新文章

  1. linux do_irq 报错 代码,linux - 内核函数asm_do_IRQ()中的irq与我在模块中请求的不同 - 堆栈内存溢出...
  2. for循环延时_RocketMQ进阶-延时消息
  3. python的缩进规则具体是什么_python语句首字缩进规则
  4. 深度 | Android 整体设计及背后意义
  5. C# NPOI(xlsx相关操作)
  6. PXE无人值守安装linux后无法启动图形
  7. charles Mock测试总结
  8. Linux平台代码覆盖率测试-.gcda/.gcno文件及其格式分析
  9. AI电话机器人源码买断 后私有云部署如何上线?
  10. 2022年最新四川建筑八大员(劳务员)模拟题库及答案
  11. 各种缩写名词汇总,力求最全面最精确
  12. TalkingData :如何做到30分钟内完成对数十亿受众数据的分析 | 会员专栏
  13. 一起来回忆一些经典的台词吧~~
  14. 典型IO模型----阻塞IO,非阻塞IO,信号驱动IO,异步IO
  15. excel自动填充脚本(awk)
  16. 基于Java+SpringBoot+Thymeleaf+Mysql新冠疫苗预约系统设计与实现
  17. 谷歌浏览器播放视频只有声音没有画面解决方法
  18. 基于STM32F103RCT6实现串口中断发送,使用环形队列
  19. 做成功的CIO从反省自己开始
  20. 牛客网oi测试赛E旅行青蛙【LIS二分】

热门文章

  1. 人啊,除了健康,什么都是浮云
  2. 将一个像素绘制到屏幕上
  3. 香港之行——大学·精神·交通
  4. Pyramid 简介
  5. Power Pivot报错:应用程序的组件中发生了未经处理的异常
  6. 苹果原壁纸高清_精选创意设计系列高清苹果手机壁纸
  7. vue项目设置服务器地址,vue项目配置后端服务器地址
  8. Unity UGUI实现图文混排
  9. 关于oracle中utl_http,Oracle使用utl_http访问webservice
  10. 谷歌浏览器不能使用opener属性的问题和解决