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/*等等,由于这部分内容过于繁多,这里先简单描述一小部分执行逻辑,之后应该还会有相关文章继续向后扩展内容。
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;
}
tty子系统通过tty_init函数完成注册过程,它在字符设备初始化函数chr_dev_init中调用,tty子系统实际上也属于字符设备
tty子系统通过tty_fops结构中的函数完成应用层的访问,如应用程序调用open(“/dev/tty”…)函数
tty_sysctl_init(); // 注册tty子系统标签(配置参数)
||
\/
register_sysctl_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);
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");
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;
#ifdef CONFIG_VT
vty_init(&console_fops);
#endif
return 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设备
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;
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、pty
panic("Couldn't register console driver\n");
kbd_init(); // 注册键盘驱动
console_map_init(); // 设置默认unicode映射
#ifdef CONFIG_MDA_CONSOLE
mda_console_init();
#endif
return 0;
}
con_ops
tty_register_driver
kbd_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;
}
for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
struct con_driver *con_driver = ®istered_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+Prtscreen
tty_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, ¶m); // 注册到通知链,表示控制台已分配
return 0;
err_free:
visual_deinit(vc);
kfree(vc);
vc_cons[currcons].d = NULL;
return err;
}
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);
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;
};
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_fops
vc_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 1
vcs_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);
}
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_fops
if (error)
goto err_unreg_char;
}
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;
}
}
}
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",其他类型为name
else
tty_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_class
dev->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=1
retval = 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);
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_DRIVER
if (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);
}