linux在tty3创建用户,我对linux理解之tty三
------------------------------------------
本文系本站原创,欢迎转载!------------------------------------------我们现在congtty core层的file operations开始分析。
static const struct file_operations tty_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,
};
先看open函数:
static int tty_open(struct inode *inode, struct file *filp)
{
int ret;
lock_kernel(); //哇哈哈,第一次看到传说中的大内核锁
ret = __tty_open(inode, filp);
unlock_kernel();
return ret;
}
转而调用__tty_open(inode, filp):
static int __tty_open(struct inode *inode, struct file *filp)
{
......
got_driver:
if (!tty) {
/* check whether we're reopening an existing tty */
tty = tty_driver_lookup_tty(driver, inode, index);
if (IS_ERR(tty)) {
mutex_unlock(&tty_mutex);
return PTR_ERR(tty);
}
}
if (tty) {
retval = tty_reopen(tty);
if (retval)
tty = ERR_PTR(retval);
} else
tty = tty_init_dev(driver, index, 0);//初始化一个tty设备,初始化线路规程和打开线路规程, 见1
mutex_unlock(&tty_mutex);
tty_driver_kref_put(driver);
if (IS_ERR(tty))
return PTR_ERR(tty);
filp->private_data = tty;//私有数据设置,read/write函数都将用到这个变量
file_move(filp, &tty->tty_files);
check_tty_count(tty, "tty_open");
if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
tty->driver->subtype == PTY_TYPE_MASTER)
noctty = 1;
#ifdef TTY_DEBUG_HANGUP
printk(KERN_DEBUG "opening %s...", tty->name);
#endif
if (!retval) {
if (tty->ops->open)
retval = tty->ops->open(tty, filp);//ops对应driver的ops,即uart_ops,也就是调用serial_core中的uart_open,见2
else
retval = -ENODEV;
}
filp->f_flags = saved_flags;
......
}
1,tty_init_dev(driver, index, 0):
struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
int first_ok)
{
......
tty = alloc_tty_struct();
if (!tty)
goto fail_no_mem;
initialize_tty_struct(tty, driver, idx);//初始化tty结构,见1-1
retval = tty_driver_install_tty(driver, tty);
if (retval < 0) {
free_tty_struct(tty);
module_put(driver->owner);
return ERR_PTR(retval);
}
/*
* Structures all installed ... call the ldisc open routines.
* If we fail here just call release_tty to clean up. No need
* to decrement the use counts, as release_tty doesn't care.
*/
retval = tty_ldisc_setup(tty, tty->link);//打开线路规程,在initialize_tty_struct中有对线路规程初始化,见1-2
if (retval)
goto release_mem_out;
return tty;
......
}
1-1,initialize_tty_struct(tty, driver, idx):
void initialize_tty_struct(struct tty_struct *tty,
struct tty_driver *driver, int idx)
{
memset(tty, 0, sizeof(struct tty_struct));
kref_init(&tty->kref);
tty->magic = TTY_MAGIC;
tty_ldisc_init(tty);//线路规程初始化,见1-1-1
tty->session = NULL;
tty->pgrp = NULL;
tty->overrun_time = jiffies;
tty->buf.head = tty->buf.tail = NULL;
tty_buffer_init(tty); //buffer初始化,见1-1-2
mutex_init(&tty->termios_mutex);
mutex_init(&tty->ldisc_mutex);
init_waitqueue_head(&tty->write_wait);
init_waitqueue_head(&tty->read_wait);
INIT_WORK(&tty->hangup_work, do_tty_hangup);
mutex_init(&tty->atomic_read_lock);
mutex_init(&tty->atomic_write_lock);
mutex_init(&tty->output_lock);
mutex_init(&tty->echo_lock);
spin_lock_init(&tty->read_lock);
spin_lock_init(&tty->ctrl_lock);
INIT_LIST_HEAD(&tty->tty_files);
INIT_WORK(&tty->SAK_work, do_SAK_work);
tty->driver = driver;
tty->ops = driver->ops;/*请注意这里的赋值,它将driver的ops赋值给了tty->ops,后面有很多用到该ops的使用,那我们
到时候要想起来它实际是对driver的ops的使用,这里driver就对应serial_core中的注册时的使用的那个driver*/
tty->index = idx;
tty_line_name(driver, idx, tty->name);
}
这个函数主要对tty_struct各个部分进行初始化。我们着重分析1-1-1和1-1-2。
1-1-1,tty_ldisc_init(tty):
void tty_ldisc_init(struct tty_struct *tty)
{
struct tty_ldisc *ld = tty_ldisc_get(N_TTY); //得到tty的线路规程,N_TTY是tty的线路规程号,内核启动时,将会注册这个N_TTY对应的ops:tty_ldisc_N_TTY
if (IS_ERR(ld))
panic("n_tty: init_tty");
tty_ldisc_assign(tty, ld); //设置线路规程给tty
}
所以经过这个初始化函数后,tty->ldisc将会被赋值为N_TTY对应的线路规程
1-1-2,tty_buffer_init(tty)
void tty_buffer_init(struct tty_struct *tty)
{//初始化buffer结构
spin_lock_init(&tty->buf.lock);
tty->buf.head = NULL;
tty->buf.tail = NULL;
tty->buf.free = NULL;
tty->buf.memory_used = 0;
INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc);//这个工作队列非常重要!主要功能是将buf数据刷到线路规程,见1-1-2-1
}
这个函数主要初始化buf结构,我们主要分析一下tty->buf.work的具体作用。
1-1-2-1,INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc)
static void flush_to_ldisc(struct work_struct *work)
{
struct tty_struct *tty =
container_of(work, struct tty_struct, buf.work.work);
unsigned long flags;
struct tty_ldisc *disc;
disc = tty_ldisc_ref(tty);//得到tty的线路规程引用
if (disc == NULL) /* !TTY_LDISC */
return;
spin_lock_irqsave(&tty->buf.lock, flags);
if (!test_and_set_bit(TTY_FLUSHING, &tty->flags)) {//设置flush标记
struct tty_buffer *head;
while ((head = tty->buf.head) != NULL) {
int count;
char *char_buf;
unsigned char *flag_buf;
count = head->commit - head->read;
if (!count) {
if (head->next == NULL)
break;
tty->buf.head = head->next;
tty_buffer_free(tty, head);
continue;
}
/* Ldisc or user is trying to flush the buffers
we are feeding to the ldisc, stop feeding the
line discipline as we want to empty the queue */
if (test_bit(TTY_FLUSHPENDING, &tty->flags))//如果想停止buffer转移到线路规程
break;
if (!tty->receive_room) { //当tty没接收空间的时候,延迟1个jiffies调用tty->buf.work,即flush_to_ldisc,把buf数据移到线路规程
schedule_delayed_work(&tty->buf.work, 1);
break;
}
if (count > tty->receive_room)
count = tty->receive_room;//不能大于接收空间的大小
char_buf = head->char_buf_ptr + head->read;
flag_buf = head->flag_buf_ptr + head->read;
head->read += count;
spin_unlock_irqrestore(&tty->buf.lock, flags);
disc->ops->receive_buf(tty, char_buf,
flag_buf, count);//执行线路规程的receive_buf,把buffer拷贝过来,这里的ops就是上面的tty_ldisc_N_TTY,见1-1-2-1-1
spin_lock_irqsave(&tty->buf.lock, flags);
}
clear_bit(TTY_FLUSHING, &tty->flags);//结束flush,设置结束标记
}
/* We may have a deferred request to flush the input buffer,
if so pull the chain under the lock and empty the queue */
if (test_bit(TTY_FLUSHPENDING, &tty->flags)) {//如果设置停止flush标记,则清空buffer
__tty_buffer_flush(tty);
clear_bit(TTY_FLUSHPENDING, &tty->flags);
wake_up(&tty->read_wait);
}
spin_unlock_irqrestore(&tty->buf.lock, flags);
tty_ldisc_deref(disc);//释放一个线路规程的引用
}
可以看出其主要作用是将tty core的buffer刷到线路规程。
1-1-2-1-1,disc->ops->receive_buf(tty, char_buf, flag_buf, count):
struct tty_ldisc_ops tty_ldisc_N_TTY = {
.magic = TTY_LDISC_MAGIC,
.name = "n_tty",
.open = n_tty_open,
.close = n_tty_close,
.flush_buffer = n_tty_flush_buffer,
.chars_in_buffer = n_tty_chars_in_buffer,
.read = n_tty_read,
.write = n_tty_write,
.ioctl = n_tty_ioctl,
.set_termios = n_tty_set_termios,
.poll = n_tty_poll,
.receive_buf = n_tty_receive_buf,
.write_wakeup = n_tty_write_wakeup
};
可以看出tty_ldisc_N_TTY->receive_buf对应于n_tty_receive_buf:
static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
const unsigned char *p;
char *f, flags = TTY_NORMAL;
int i;
char buf[64];
unsigned long cpuflags;
if (!tty->read_buf)
return;
if (tty->real_raw) {
spin_lock_irqsave(&tty->read_lock, cpuflags);
i = min(N_TTY_BUF_SIZE - tty->read_cnt,
N_TTY_BUF_SIZE - tty->read_head);
i = min(count, i);//还是先确定要拷贝数据的长度小
memcpy(tty->read_buf + tty->read_head, cp, i);//拷贝到read buf
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
cp += i;
count -= i;
i = min(N_TTY_BUF_SIZE - tty->read_cnt,
N_TTY_BUF_SIZE - tty->read_head);
i = min(count, i);
memcpy(tty->read_buf + tty->read_head, cp, i);
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
spin_unlock_irqrestore(&tty->read_lock, cpuflags);
/*这里你可能奇怪为什么要拷贝两次呢?是因为数据读取缓存read_buf(0~N_TTY_BUF_SIZE)为一环形缓冲区。tty->read_tail, tty->read_tail指向第一个未被读取的数据,
tty->read_cnt缓存中的数据,tty->read_head指向 第一个未被占用的空间。由于是环形缓存tty->read_cnt不一定等于tty->read_head - tty->read_tail。
tty->read_head可能小于tty->read_tail所以可能有以下关系:
tty->read_cnt = N_TTY_BUF_SIZE - tty->read_tail + tty->read_head。
所以将read_buf中的值考到用户空间需要考两次,*nr的值可能大于N_TTY_BUF_SIZE - tty->read_tail
而小于tty->read_cnt。拷数据时是从tty->read_tail开始,第一次考取N_TTY_BUF_SIZE - tty->read_tail, 第二次在read_buf的开始位置到tty->read_head之间获取还需的数据。*/
} else {
for (i = count, p = cp, f = fp; i; i--, p++) {
if (f)
flags = *f++;
switch (flags) {
case TTY_NORMAL:
n_tty_receive_char(tty, *p);
break;
case TTY_BREAK:
n_tty_receive_break(tty);
break;
case TTY_PARITY:
case TTY_FRAME:
n_tty_receive_parity_error(tty, *p);
break;
case TTY_OVERRUN:
n_tty_receive_overrun(tty);
break;
default:
printk(KERN_ERR "%s: unknown flag %d\n",
tty_name(tty, buf), flags);
break;
}
}
if (tty->ops->flush_chars)
tty->ops->flush_chars(tty);
}
n_tty_set_room(tty);//将剩下的空间赋值给tty->receive_room,以备查询
if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) {
kill_fasync(&tty->fasync, SIGIO, POLL_IN);//向用户空间发一个异步信号
if (waitqueue_active(&tty->read_wait))
wake_up_interruptible(&tty->read_wait);//唤醒读进程
}
/*
* Check the remaining room for the input canonicalization
* mode. We don't want to throttle the driver if we're in
* canonical mode and don't have a newline yet!
*/
if (tty->receive_room < TTY_THRESHOLD_THROTTLE)
tty_throttle(tty);//指示空间低于预定阀值
}
这个函数主要是将tty core层的数据刷到read_buf环形缓冲区中,下面我们回到tty_init_dev中的1-2部分。
1-2,tty_ldisc_setup(tty, tty->link)
int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
{
struct tty_ldisc *ld = tty->ldisc;//tty结构初始化的时候已经对它赋值,故这里可以使用
int retval;
retval = tty_ldisc_open(tty, ld);//如果是N_TTY,则对应的ops是tty_ldisc_N_TTY
if (retval)
return retval;
if (o_tty) {
retval = tty_ldisc_open(o_tty, o_tty->ldisc);//打开配对的tty
if (retval) {
tty_ldisc_close(tty, ld);
return retval;
}
tty_ldisc_enable(o_tty); //使能线路规程
}
tty_ldisc_enable(tty);
return 0;
}
这里通过线路规程的ops的open函数打开该tty。
2,tty->ops->open(tty, filp)
从initialize_tty_struct分析中知道,这里的ops就是driver对应的ops,也就是serial中的uart_ops,那我们看下uart_ops的open的函数:
static int uart_open(struct tty_struct *tty, struct file *filp)
{
......
/*
* Start up the serial port.
*/
retval = uart_startup(state, 0);//这是open的核心操作,见2-1
......
}
2-1,uart_startup(state, 0)
static int uart_startup(struct uart_state *state, int init_hw)
{
......
retval = port->ops->startup(port);//调用port口的ops,即mxc_ops,主要是对uart初始化,见2-1-1
if (retval == 0) {
if (init_hw) {//从open函数传下来的是0,这里不执行,但其它情况不一定
/*
* Initialise the hardware port settings.
*/
uart_change_speed(state, NULL);
/*
* Setup the RTS and DTR signals once the
* port is open and ready to respond.
*/
if (info->port.tty->termios->c_cflag & CBAUD)
uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR);
}
if (info->flags & UIF_CTS_FLOW) {
spin_lock_irq(&port->lock);
if (!(port->ops->get_mctrl(port) & TIOCM_CTS))
info->port.tty->hw_stopped = 1;
spin_unlock_irq(&port->lock);
}
info->flags |= UIF_INITIALIZED;
clear_bit(TTY_IO_ERROR, &info->port.tty->flags);
}
if (retval && capable(CAP_SYS_ADMIN))
retval = 0;
return retval;
}
2-1-1,port->ops->startup(port)
port口子的ops在mxc_uart中,
static struct uart_ops mxc_ops = {
.tx_empty = mxcuart_tx_empty,
.set_mctrl = mxcuart_set_mctrl,
.get_mctrl = mxcuart_get_mctrl,
.stop_tx = mxcuart_stop_tx,
.start_tx = mxcuart_start_tx,
.stop_rx = mxcuart_stop_rx,
.enable_ms = mxcuart_enable_ms,
.break_ctl = mxcuart_break_ctl,
.startup = mxcuart_startup,
.shutdown = mxcuart_shutdown,
.set_termios = mxcuart_set_termios,
.type = mxcuart_type,
.pm = mxcuart_pm,
.release_port = mxcuart_release_port,
.request_port = mxcuart_request_port,
.config_port = mxcuart_config_port,
.verify_port = mxcuart_verify_port,
.send_xchar = mxcuart_send_xchar,
};
我们看startup对应到了mxcuart_startup:
static int mxcuart_startup(struct uart_port *port)
{
uart_mxc_port *umxc = (uart_mxc_port *) port;
int retval;
volatile unsigned int cr, cr1 = 0, cr2 = 0, ufcr = 0;
/*
* Some UARTs need separate registrations for the interrupts as
* they do not take the muxed interrupt output to the ARM core
*/
if (umxc->ints_muxed == 1) {//从设备定义看,这个值为1
retval = request_irq(umxc->port.irq, mxcuart_int, 0,//定义中断函数
"mxcintuart", umxc);
if (retval != 0) {
return retval;
}
} else {
retval = request_irq(umxc->port.irq, mxcuart_tx_int,
0, "mxcintuart", umxc);
if (retval != 0) {
return retval;
} else {
retval = request_irq(umxc->irqs[0], mxcuart_rx_int,
0, "mxcintuart", umxc);
if (retval != 0) {
free_irq(umxc->port.irq, umxc);
return retval;
} else {
retval =
request_irq(umxc->irqs[1], mxcuart_mint_int,
0, "mxcintuart", umxc);
if (retval != 0) {
free_irq(umxc->port.irq, umxc);
free_irq(umxc->irqs[0], umxc);
return retval;
}
}
}
}
/* Initialize the DMA if we need SDMA data transfer */
if (umxc->dma_enabled == 1) {//是否需要dma传输
retval = mxcuart_initdma(dma_list + umxc->port.line, umxc);
if (retval != 0) {
printk
(KERN_ERR
"MXC UART: Failed to initialize DMA for UART %d\n",
umxc->port.line);
mxcuart_free_interrupts(umxc);
return retval;
}
/* Configure the GPR register to receive SDMA events */
config_uartdma_event(umxc->port.line);
}
/*
* Clear Status Registers 1 and 2
*/
writel(0xFFFF, umxc->port.membase + MXC_UARTUSR1);
writel(0xFFFF, umxc->port.membase + MXC_UARTUSR2);
/* Configure the IOMUX for the UART */
gpio_uart_active(umxc->port.line, umxc->ir_mode);
/*
* Set the transceiver invert bits if required
*/
if (umxc->ir_mode == IRDA) {
echo_cancel = 1;
writel(umxc->ir_rx_inv | MXC_UARTUCR4_IRSC, umxc->port.membase
+ MXC_UARTUCR4);
writel(umxc->rxd_mux | umxc->ir_tx_inv,
umxc->port.membase + MXC_UARTUCR3);
} else {
writel(umxc->rxd_mux, umxc->port.membase + MXC_UARTUCR3);
}
/*
* Initialize UCR1,2 and UFCR registers
*/
if (umxc->dma_enabled == 1) {
cr2 = (MXC_UARTUCR2_TXEN | MXC_UARTUCR2_RXEN);
} else {
cr2 =
(MXC_UARTUCR2_ATEN | MXC_UARTUCR2_TXEN | MXC_UARTUCR2_RXEN);
}
writel(cr2, umxc->port.membase + MXC_UARTUCR2);
/* Wait till we are out of software reset */
do {
cr = readl(umxc->port.membase + MXC_UARTUCR2);
} while (!(cr & MXC_UARTUCR2_SRST));
if (umxc->mode == MODE_DTE) {
ufcr |= ((umxc->tx_threshold << MXC_UARTUFCR_TXTL_OFFSET) |
MXC_UARTUFCR_DCEDTE | MXC_UARTUFCR_RFDIV | umxc->
rx_threshold);
} else {
ufcr |= ((umxc->tx_threshold << MXC_UARTUFCR_TXTL_OFFSET) |
MXC_UARTUFCR_RFDIV | umxc->rx_threshold);
}
writel(ufcr, umxc->port.membase + MXC_UARTUFCR);
/*
* Finally enable the UART and the Receive interrupts
*/
if (umxc->ir_mode == IRDA) {
cr1 |= MXC_UARTUCR1_IREN;
}
if (umxc->dma_enabled == 1) {
cr1 |= (MXC_UARTUCR1_RXDMAEN | MXC_UARTUCR1_ATDMAEN |
MXC_UARTUCR1_UARTEN);
} else {
cr1 |= (MXC_UARTUCR1_RRDYEN | MXC_UARTUCR1_UARTEN);
}
writel(cr1, umxc->port.membase + MXC_UARTUCR1);
return 0;
}
我们看下这个mxcuart_startup,主要是根据device定义对uart的初始化。
综上,tty_open的流程是tty_core->tty_ldisc和tty_core->serial_core->mxc_uart。它主要执行了tty_init_dev(driver, index, 0),这个函数主要是初始化一个tty设备,初始化线路规程和打开线路规程。还有执行了tty->ops->open(tty, filp),主要初始化了要使用的uart口子,有了这个基础,我们就可以对tty进行读写了。
阅读(491) | 评论(0) | 转发(0) |
linux在tty3创建用户,我对linux理解之tty三相关推荐
- Linux操作系统——批量创建用户
Linux操作系统--批量创建用户 文章目录 Linux操作系统--批量创建用户 第一步:创建组群GID为650的是student的组群 第二步:创建用户信息文件students.txt,并用vim编 ...
- 在linux系统中 创建用户账户的同时,在Linux系统中大批量建立帐户
在Linux系统中大批量建立帐户 企业如果想在Linux操作系统上部署文件的话,可能需要一次性建立大量的帐户.如为了加强文件的管理力度,需要为每个员工配置一个帐户.如此的话,就可以针对员工进行权限控制 ...
- 如何创建一个linux用户名和密码,Linux下如何创建用户 | Soo Smart!
Linux下如何创建用户? 初步接触linux时要学会用户账号的添加.删除与修改.用户口令的管理.用户组的管理方法,这里列出来供大家参考使用吧. user的create, delete, modify ...
- linux批量创建用户1000,Linux下批量创建用户
Linux下批量创建用户主要有以下两种方法: 方法一: 1,新建一个文件user.txt,以/etc/passwd 为模板 2,再次新建一个文件passwd..txt,以/etc/shadow 为模板 ...
- Linux:如何创建用户
概述 下面将演示创建用户 zyq01 (1)输入命令:sudo useradd zyq01,回车,创建用户: (2)输入命令:ls,回车,查看用户是否创建成功(可以看到用户已经创建成功了): (3)输 ...
- linux怎么创建用户教程,在Linux中如何手动创建一个用户
1.首先要明白用useradd创建用户的时候会更改添加5个地方的内容 (1)/etc/passwd //比如创建useradd 111 // [root@localhost ...
- suse linux 创建用户密码,suse linux上创建用户方式
当需要数据共享时,在suse linux上创建用户需要注意以下两点: 1. 所有服务器相同的用户名具备相同的id号. 2. 所有用户属于同一个组(如users组). 如同一台机器上: 1. 创建一个I ...
- linux批量创建系统,linux系统批量创建用户
脚本目的:批量创建linux系统用户 说明:要创建用户的主机密码写入到ip.txt文件中 [root@thsf02 scripts]# cat ip.txt 10.165.123.0 10.172.4 ...
- Linux shell简单创建用户脚本
前面介绍简单的shell编写规则. 现在开始编写一个简单的shell脚本. Linux shell介绍 编写shell脚本 1.创建脚本文件 2.根据需求,编写脚本 3.测试执行脚本 ...
最新文章
- 【PHPWord】TextRun
- ios JSON 解析流程(转)
- python正则表达式实例教程_Python正则表达式经典入门教程
- redis订阅执行一段时间自动停止_面试系列 redis 分布式锁amp;数据一致性
- 【强化学习】A3C原理
- mysql 阿里云 版本_阿里云虚拟主机mysql已经支持版本切换,支持MySQL 5.7.25
- NGUI_2.6.3_系列教程三
- 《高效能程序员的修炼》一第2章 把一堆烂事搞定的艺术
- 13.TCP/IP 详解卷1 --- IGMP : Internet 管理组协议
- oracle分组取第N条,ROW_NUMBER() OVER的用法
- 优秀网站源码、编程源码大全
- 手把手教你Excel数据处理!
- linux版百度导航软件,百度导航2019新版
- 什么是实体关系图(ERD)?
- Android 文件管理器的列表界面
- 从pdf复制文字到word中的问题
- gridsome(三)——plugins
- Pandas数据分析库(2)Python数据分析
- OpenStack T版服务组件之Nova计算服务
- 【虹科分享】在容器上使用 ntop 工具的最佳实践