Linux tty core 源码分析.docx
《Linux tty core 源码分析.docx》由会员分享,可在线阅读,更多相关《Linux tty core 源码分析.docx(56页珍藏版)》请在冰点文库上搜索。
Linuxttycore源码分析
Linuxttycore源码分析
[日期:
2011-02-08]
来源:
CSDN 作者:
sirzjp
本文以linux2.6.27内核为基础,阅读ttycore源码并作注解,自己接触时间不长,希望与爱好者共同分享,错误之处还望指正。
linuxttycore是建立在字符设备驱动的基础之上,并为tty类型设备(串口、控制台、虚拟终端)提供一个公用的平台。
所以任何一个tty设备驱动的注册都是作为一个字符设备驱动而操作的。
下面我们看看代码中是如何处理的:
/*3/2004jmc:
whydothesedevicesexist?
*/
//tty核心默认在内核中实现的字符型tty设备驱动
staticstructcdevtty_cdev,console_cdev;
#ifdefCONFIG_UNIX98_PTYS
staticstructcdevptmx_cdev;
#endif
#ifdefCONFIG_VT
staticstructcdevvc0_cdev;
#endif
/*
*Ok,nowwecaninitializetherestofthettydevicesandcancount
*onmemoryallocations,interruptsetc..
*/
staticint__inittty_init(void)
{
//在字符设备模型中加入注册tty_cdev驱动并加入/dev/tty这样的设备
cdev_init(&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)
panic("Couldn'tregister/dev/ttydriver\n");
device_create_drvdata(tty_class,NULL,MKDEV(TTYAUX_MAJOR,0),NULL,
"tty");
//在字符设备模型中加入注册console_cdev驱动并加入/dev/console这样的设备
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'tregister/dev/consoledriver\n");
device_create_drvdata(tty_class,NULL,MKDEV(TTYAUX_MAJOR,1),NULL,
"console");
//在字符设备模型中加入注册ptmx_cdev驱动并加入/dev/ptmx这样的设备
#ifdefCONFIG_UNIX98_PTYS
cdev_init(&ptmx_cdev,&ptmx_fops);
if(cdev_add(&ptmx_cdev,MKDEV(TTYAUX_MAJOR,2),1)||
register_chrdev_region(MKDEV(TTYAUX_MAJOR,2),1,"/dev/ptmx")<0)
panic("Couldn'tregister/dev/ptmxdriver\n");
device_create_drvdata(tty_class,NULL,MKDEV(TTYAUX_MAJOR,2),NULL,"ptmx");
#endif
//在字符设备模型中加入注册vc0_cdev驱动并加入/dev/tty0这样的设备
#ifdefCONFIG_VT
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'tregister/dev/tty0driver\n");
device_create_drvdata(tty_class,NULL,MKDEV(TTY_MAJOR,0),NULL,"tty0");
vty_init();//这里暂时不做解释
#endif
return0;
}
//上面是本身加入的,也就是我们系统一般都有的设备,而且这几种设备都是指向其他的设备,在tty_open中我们将看到以后都会指向其
//他具体的设备。
对这几种设备大多数人都存在一定的混淆,这里我就自己的理解解释下:
///dev/console(5,1)表示系统的控制台
///dev/tty(5,0)表示进程的控制终端
///dev/tty0(4,0)表示当前使用的虚拟终端
//这样的解释也不是很清楚,这得从历史说起,以前计算机还是比较昂贵的时候,一台电脑上一般接有很多键盘与显示器的组合设备用以
//操作计算机这样的组合设备就是所谓的终端,而还存在一种直接和电脑连接键盘和显示器这就是控制台。
而现在的应用环境发生了变化,
//一般把能直接显示系统信息的终端称呼为系统控制台,而其他设备则称呼虚拟终端。
也就是当前虚拟终端作控制台。
staticconststructfile_operationstty_fops={
.llseek =no_llseek,
.read =tty_read,
.write =tty_write,
.poll =tty_poll,
.unlocked_ioctl=tty_ioctl,
.compat_ioctl=tty_compat_ioctl,
.open =tty_open,
.release=tty_release,
.fasync =tty_fasync,
};
#ifdefCONFIG_UNIX98_PTYS
staticconststructfile_operationsptmx_fops={
.llseek =no_llseek,
.read =tty_read,
.write =tty_write,
.poll =tty_poll,
.unlocked_ioctl=tty_ioctl,
.compat_ioctl=tty_compat_ioctl,
.open =ptmx_open,
.release=tty_release,
.fasync =tty_fasync,
};
#endif
staticconststructfile_operationsconsole_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,
};
//从上面看几个驱动的操作函数大致相同,只有ptmx_fops的open方法和console_fops的write方法不同其他操作都是相同的所以在其
//他操作上要兼顾各种设备
//下面我们介绍下tty_driver结构和tty_struct结构。
tty_driver表示一个具体的tty设备的驱动程序,而tty_struct表示tty设备在
//具体的分析中介绍其成员。
structtty_driver{
intmagic; /*magicnumberforthisstructure*/
structkrefkref;/*Referencemanagement*/
structcdevcdev;//可见tty设备驱动是一个字符设备设备驱动
structmodule*owner;
constchar*driver_name;//这里是指驱动程序的名字
constchar*name;//tty设备的命名相关
intname_base;/*offsetofprintedname*/
intmajor; /*majordevicenumber*/
intminor_start;/*startofminordevicenumber*/
intminor_num;/*numberof*possible*devices*/
intnum; /*numberofdevicesallocated*/
shorttype; /*typeofttydriver*/
shortsubtype;/*subtypeofttydriver*/
structktermiosinit_termios;/*Initialtermios*/
intflags; /*ttydriverflags*/
structproc_dir_entry*proc_entry;/*/procfsentry*/
structtty_driver*other;/*onlyusedforthePTYdriver*/
/*
*Pointertothettydatastructures
*/
structtty_struct**ttys;//驱动操作的具体tty设备
structktermios**termios;
structktermios**termios_locked;
void*driver_state;
/*
*Drivermethods
*/
conststructtty_operations*ops;
structlist_headtty_drivers;//用于链接tty驱动全局链表
};
//下面对tty_open函数进行分析,open函数的具体操作就是初始化tty_struct结构并作为赋值filp->private_data,为后续的操作做准备
/**
*tty_open -openattydevice
*@inode:
inodeofdevicefile
*@filp:
filepointertotty
*
*tty_openandtty_releasekeepupthettycountthatcontainsthe
*numberofopensdoneonatty.Wecannotusetheinode-count,as
*differentinodesmightpointtothesametty.
*
*Open-countingisneededforptymasters,aswellasforkeeping
*trackofseriallines:
DTRisdroppedwhenthelastclosehappens.
*(Thisisnotdonesolelythroughtty->count,now. -Ted1/27/92)
*
*Thetermiosstateofaptyisresetonfirstopensothat
*settingsdon'tpersistacrossreuse.
*
*Locking:
tty_mutexprotectstty,get_tty_driverandinit_devwork.
* tty->countshouldprotecttherest.
* ->siglockprotects->signal/->sighand
*/
staticint__tty_open(structinode*inode,structfile*filp)
{
structtty_struct*tty;
intnoctty,retval;
structtty_driver*driver;
intindex;
dev_tdevice=inode->i_rdev;//得到设备的设备号
unsignedshortsaved_flags=filp->f_flags;
nonseekable_open(inode,filp);//主要是设置file->f_mode的一些标志使文件不可定位
retry_open:
noctty=filp->f_flags&O_NOCTTY;//是否终端绑定到指定的进程
index =-1;
retval=0;
mutex_lock(&tty_mutex);
//这里表示的是设备号为(5,0)及/dev/tty是进程控制终端的别名,因此是一个要找到真正的设备
if(device==MKDEV(TTYAUX_MAJOR,0)){
tty=get_current_tty();
if(!
tty){//进程没有控制终端则直接返回
mutex_unlock(&tty_mutex);
return-ENXIO;
}
driver=tty->driver;
index=tty->index;//设备在驱动中的driver->ttys中的位置
filp->f_flags|=O_NONBLOCK;/*Don'tlet/dev/ttyblock*/
/*noctty=1;*/
gotogot_driver;
}
#ifdefCONFIG_VT
//这里表示的是设备号为(4,0)及/dev/tty0是当前进程使用虚拟终端的别名,因此是一个要找到真正的设备
if(device==MKDEV(TTY_MAJOR,0)){
externstructtty_driver*console_driver;//控制台的驱动
driver=console_driver;
index=fg_console;//表示当前控制台索引
noctty=1;
gotogot_driver;
}
#endif
//这里表示的是设备号为(5,1)及/dev/console是指系统控制台,因此是一个要找到真正的设备
if(device==MKDEV(TTYAUX_MAJOR,1)){
driver=console_device(&index);
if(driver){
/*Don'tlet/dev/consoleblock*/
filp->f_flags|=O_NONBLOCK;
noctty=1;
gotogot_driver;
}
mutex_unlock(&tty_mutex);
return-ENODEV;
}
//依据设备的设备号来确定设备在具体驱动中的索引。
driver=get_tty_driver(device,&index);
if(!
driver){
mutex_unlock(&tty_mutex);
return-ENODEV;
}
/*到此位置我们的目的是确定设备驱动和设备在驱动中的位置以及设备是否和进程绑定*/
got_driver:
//init_dev初始化一个tty_struct结构具体在下面分析
retval=init_dev(driver,index,&tty);
mutex_unlock(&tty_mutex);
if(retval)
returnretval;
filp->private_data=tty;//把tty_struct结构作为file->private_data这也是open函数的主要目的
file_move(filp,&tty->tty_files);
check_tty_count(tty,"tty_open");//统计tty设备打开的次数
if(tty->driver->type==TTY_DRIVER_TYPE_PTY&&
tty->driver->subtype==PTY_TYPE_MASTER)
noctty=1;
#ifdefTTY_DEBUG_HANGUP
printk(KERN_DEBUG"opening%s...",tty->name);
#endif
if(!
retval){
if(tty->ops->open)
retval=tty->ops->open(tty,filp);//调用tty_operations中的回调函数tty驱动中我们要实现的%%%%%
else
retval=-ENODEV;
}
filp->f_flags=saved_flags;
if(!
retval&&test_bit(TTY_EXCLUSIVE,&tty->flags)&&//设备是否为互斥设备
!
capable(CAP_SYS_ADMIN))
retval=-EBUSY;
if(retval){
#ifdefTTY_DEBUG_HANGUP
printk(KERN_DEBUG"error%dinopening%s...",retval,
tty->name);
#endif
release_dev(filp);//init_dev的反操作
if(retval!
=-ERESTARTSYS)
returnretval;
if(signal_pending(current))
returnretval;
schedule();
/*
*Needtoresetf_opincaseahanguphappened.
*/
if(filp->f_op==&hung_up_tty_fops)
filp->f_op=&tty_fops;
gotoretry_open;
}
mutex_lock(&tty_mutex);
spin_lock_irq(¤t->sighand->siglock);
if(!
noctty&& //设备和进程绑定作为进程回话的控制终端
current->signal->leader&&
!
current->signal->tty&&
tty->session==NULL)
__proc_set_tty(current,tty);
spin_unlock_irq(¤t->sighand->siglock);
mutex_unlock(&tty_mutex);
return0;
}
/*BKLpushdown:
scarycodeavoidancewrapper*/
staticinttty_open(structinode*inode,structfile*filp)
{
intret;
lock_kernel();
ret=__tty_open(inode,filp);
unlock_kernel();
returnret;
}
/**
*init_dev -initialiseattydevice
*@driver:
ttydriverweareopeningadeviceon
*@idx:
deviceindex
*@tty:
returnedttystructure
*
*Prepareattydevice.Thismaynotbea"new"cleandevicebut
*couldalsobeanactivedevice.Theptydriversrequirespecial
*handlingbecauseofthis.
*
*Locking:
* Thefunctioniscalledunderthetty_mutex,which
*protectsusfromthettystructordriveritselfgoingaway.
*
*Onexitthettydevicehasthelinedisciplineattachedand
*areferencecountof1.Ifapairwascreatedforpty/ttyuse
*andtheotherwasaptymasterthenittoohasareferencecountof1.
*
*WSH06/09/97:
Rewrittentoremoveracesandproperlycleanupaftera
*failedopen. Thenewcodeprotectstheopenwithamutex,soit's
*reallyquitestraightforward. Themutexlockingcanprobablybe
*relaxedforthe(mostcommon)caseofreopeningatty.
*/
staticintinit_dev(structtty_driver*driver,i