当前位置: 首页 > wzjs >正文

做网站兼职您的网站空间即将过期

做网站兼职,您的网站空间即将过期,北京做网站哪家公司好,国外网站三维特效教程注:本文为 “Linux 终端模式” 相关文章合辑。 略作重排,如有内容异常,请看原文。 终端输入输出的三种模式 guidao 1 前言 在进行项目开发时,需要实时读取终端输入(无需按下 Enter 键即可读取)。然而&a…

注:本文为 “Linux 终端模式” 相关文章合辑

略作重排,如有内容异常,请看原文。


终端输入输出的三种模式

guidao

1 前言

在进行项目开发时,需要实时读取终端输入(无需按下 Enter 键即可读取)。然而,Go 语言并未提供便捷的接口。为此,查阅了相关资料,对终端的 I/O 进行了深入研究,并将关键内容整理如下。

2 终端 I/O 的三种模式

2.1 Canonical 模式

该模式也称为 cooked 模式 。在这种模式下,终端每次返回一行数据,所有特殊字符均会被解释(例如:^C)。

2.2 Nocanonical 模式

此模式亦称 raw 模式 。在这种模式下,终端每次返回一个字符,而非先收集一行数据再返回。特殊字符不会被特殊处理,需自行处理。例如,终端编辑器 vi 即处于这种模式下,可完全控制输入输出字符。

2.3 Cbreak 模式

该模式与 raw 模式相似,但会处理特殊字符(某些场景下需使用此模式)。

3 终端控制结构

终端设备的所有可控制属性均通过以下结构进行管理。Go 语言中,该结构定义于 syscall 包中。

struct termios {tcflag_t    c_iflag;    /* 输入标志 */tcflag_t    c_oflag;    /* 输出标志 */tcflag_t    c_cflag;    /* 控制标志 */tcflag_t    c_lflag;    /* 本地标志 */cc_t        c_cc[NCCS]; /* 控制字符 */
};

其中,c_iflag 控制输入属性(例如:将 CR 映射为 NL),c_oflag 控制输出属性(例如:将 tab 扩展为空格),c_cflag 控制行属性,c_lflag 设置用户与设备接口属性(例如:本地回显、允许信号产生)。该结构通过两个函数进行获取与设置,函数声明如下:

#include <termios.h>
int tcgetattr(int filedes, struct termios *termptr);
int tcsetattr(int filedes, int opt, const struct termios *termptr);
% 返回值:若成功则返回 0,若失败则返回 -1

filedes 是文件描述符,通常通过打开设备文件 /dev/tty 获得。tcsetattr 函数中的 opt 参数可取以下值:

  • TCSANOW :使设置立即生效。
  • TCSADRAIN :在输出缓存区输出到屏幕后生效。
  • TCSAFLUSH :在输出缓存区输出到屏幕后生效,并且丢弃所有输入缓存中未处理的数据。

4 纯 Go 实现一个 getchar(可立即获取用户输入而无需按下 Enter 键)

package mainimport ("fmt""os""syscall""unsafe"
)% 通过系统调用设置属性
func ioctl(fd, request, argp uintptr) error {if _, _, e := syscall.Syscall6(syscall.SYS_IOCTL, fd, request, argp, 0, 0, 0); e != 0 {return e}return nil
}% 获取设备属性
func Tcgetattr(fd uintptr, argp *syscall.Termios) error {return ioctl(fd, syscall.TIOCGETA, uintptr(unsafe.Pointer(argp)))
}% 设置终端
func Tcsetattr(fd, opt uintptr, argp *syscall.Termios) error {return ioctl(fd, opt, uintptr(unsafe.Pointer(argp)))
}func main() {var term syscall.Termiosvar origin syscall.Termiosfd, err := os.Open("/dev/tty")must(err)err = Tcgetattr(fd.Fd(), &term)origin = termmust(err)% 设置为nobufferterm.Lflag &^= syscall.ICANONterm.Cc[syscall.VMIN] = 1term.Cc[syscall.VTIME] = 0Tcsetattr(fd.Fd(), syscall.TIOCSETA, &term)c, err := ReadChar(fd.Fd())must(err)fmt.Println(" read:", string(c))% 恢复原来的设置Tcsetattr(fd.Fd(), syscall.TIOCSETA, &origin)
}func ReadChar(fd uintptr) ([]byte, error) {b := make([]byte, 4)n, e := syscall.Read(int(fd), b)if e != nil {return nil, e}return b[:n], nil
}
func must(err error) {if err != nil {panic(err)}
}

5 参考资料

  • http://www.lafn.org/~dave/linux/terminalIO.html

终端的 Raw Mode

Erzbir

2025-02-10

Raw Mode

在启动 Terminal 时,默认是以 canonical mode 或者叫 cooked mode 启动的。这种模式下,用户按键产生的字符会由终端驱动存入内部缓冲区,期间可以自动处理 BackspaceCtrl-C 等特殊字符,需要等待用户按下 Enter 后才将字符从缓冲区中送到程序的标准输入(STDIN_FILENO 为 0)。

例如下面这个程序:

#include <unistd.h>int main() {char c;while (read(STDIN_FILENO, &c, 1) == 1) {write(STDOUT_FILENO, &c, 1);}return 0;
}

终端默认是 canonical mode ,所以会在按下 Enter 后,从标准输入中每次读取一个字节到 c ,直到 0 个字节可读。

这也是常用的一种模式,输入命令及参数回车,然后 shell 就会解释我们的命令。但是你可能见过一些命令行程序比如 top ,按下 q 后并没有 Enter 就直接退出了,这就需要 raw mode 让我们自己来处理这些输入。

下面这个例子仍然是 canonical mode 的,也是通过 q 来退出,但是需要 Enter

#include <unistd.h>int main() {char c;while (read(STDIN_FILENO, &c, 1) == 1 && c != 'q') {write(STDOUT_FILENO, &c, 1);}return 0;
}

但是这样的问题就在于和预期稍微有些差别,我们不仅要 Enter ,而且那之后的字符可能是 wqeqweq 这样,而这个程序是读到一个 q char 就会退出。

而在 raw mode 下,没有回显,不会处理特殊字符,也没有缓冲区。

关闭回显

要切换到 raw mode 首先是关闭回显,需要用到 termios.h 提供的 tcgetattr() 函数以及 tcsetattr() 函数。

这两个函数的定义是这样的:

% The tcgetattr() function copies the parameters associated with the terminal referenced by fildes in the termios structure referenced by termios_p.
% This function is allowed from a background process; however, the terminal attributes may be subsequently changed by a foreground process.int tcgetattr(int, struct termios *);% The tcsetattr() function sets the parameters associated with the terminal from the termios structure referenced by termios_p.
% The optional_actions field is created by or'ing the following values, as specified in the include file ⟨termios.h⟩.int tcsetattr(int, int, const struct termios *);

通过下面这三个语句就可以获取到当前的状态:

struct termios raw;
tcgetattr(STDIN_FILENO, &raw);
printf("%lu", raw.c_lflag);

我们需要的就是这个 c_lflag ,将这个 flag 进行位运算后再调用 tcsetattr() 设置即可:

void enable_raw_mode() {struct termios raw;tcgetattr(STDIN_FILENO, &raw);raw.c_lflag &= ~(ECHO);tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}

这样就关闭了回显,运行之后输入字符就不会再有回显。如果你发现还是有回显,确保在终端运行,而不是用 IDE 的 run 来运行或是其他的一些 wrap。

切换到 Raw Mode

我们已经关闭了回显,现在还需要禁用缓冲区和禁用特殊字符处理等等操作,下面一步一步来。

禁用 SIGINT 和 SIGSTP

ISIG 控制是否发送 SIGINTSIGSTP

void enable_raw_mode() {struct termios raw;tcgetattr(STDIN_FILENO, &raw);raw.c_lflag &= ~(ECHO | ICANON | ISIG);tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}

现在,不需要回车且 Ctrl-CCtrl-Z 的输入已经不起作用。

禁用输出流控^Software flow control^

通过 flag: c_iflagIXON 控制。

void enable_raw_mode() {struct termios raw;tcgetattr(STDIN_FILENO, &raw);raw.c_lflag &= ~(ECHO | ICANON | ISIG);raw.c_iflag &= ~(IXON);tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}

禁用 Ctrl-V

通过 c_lflagIEXTEN 控制。

void enable_raw_mode() {struct termios raw;tcgetattr(STDIN_FILENO, &raw);raw.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN);raw.c_iflag &= ~(IXON);tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}

关闭换行映射

c_lflagICRNL

\r 的 char 值是 13 ,\n 的 char 值是 10 。

为了消除不同操作系统之间换行符的差异,默认开启映射,可能会将 \r 自动转换成 \n ,也就是说 Ctrl-JCtrl-M 会是一样的值,而 Ctrl-M 原本是 13 。

void enable_raw_mode() {struct termios raw;tcgetattr(STDIN_FILENO, &raw);raw.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN);raw.c_iflag &= ~(IXON | ICRNL);tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}

关闭输出处理

c_oflagOPOST

开启时终端会将换行符 \n 转换成回车符后跟一个换行符:\r\n

void enable_raw_mode() {struct termios raw;tcgetattr(STDIN_FILENO, &raw);raw.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN);raw.c_iflag &= ~(IXON | ICRNL);raw.c_oflag &= ~(OPOST);tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}

实际上还需要更多的操作,比如通过结构体中的 c_cc 来设置 read() 返回所需要的最小字节数以及读超时时间等:

void enable_raw_mode() {struct termios raw;tcgetattr(STDIN_FILENO, &raw);% 修改 raw.c_lflag 来禁用终端的一些本地模式% 禁用 ECHO: 关闭回显% 禁用 ICANON: 关闭规范模式 (行缓冲), 使得输入按字符处理% 禁用 IEXTEN: 禁用扩展输入处理% 禁用 ISIG: 禁用信号生成 (例如 Ctrl+C 不再生成 SIGINT 信号)raw.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN);% 修改 raw.c_iflag 来禁用某些输入处理% 禁用 BRKINT, ICRNL, INPCK, ISTRIP, IXON 等标志raw.c_iflag &= ~(IXON | ICRNL | BRKINT | INPCK | ISTRIP);% 修改 raw.c_oflag 来禁用输出处理 (比如自动转换换行符)raw.c_oflag &= ~(OPOST);% 修改 raw.c_cflag" 设置字符大小为 8(通常为 CS8)raw.c_cflag |= (CS8);% 设置控制字符% VMIN = 0: read() 至少读取 0 个字节% VTIME = 1000: read() 超时时间为 1000msraw.c_cc[VMIN] = 0;raw.c_cc[VTIME] = 1000;tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}int main() {char c;enable_raw_mode();while (read(STDIN_FILENO, &c, 1) == 1 && c != 'q') {if (c == '\r' | c == '\n') {write(STDOUT_FILENO, "\r\n", 2);}write(STDOUT_FILENO, &c, 1);}return 0;
}

这里就设置了 1000 秒后如果没有读取到 read() 就会直接返回,如果 c_cc[VMIN] 为 1 则是会阻塞到读取到一个字节为止。

  • BRKINT :当检测到 break 条件时触发中断行为或产生错误,帮助捕捉和处理输入中断。
  • INPCK :启用输入奇偶校验,用于检测数据传输中的错误。
  • ISTRIP :对输入数据进行剥离,将每个字节的最高位清零,确保只保留 7 位数据。

直接切换到 Raw Mode

实际上 termios.h 中有提供一个直接切换的 API :cfmakeraw()

这个函数只需要传入一个 struct termios ,会修改结构体中的值就像我们上面做的那样。

void enable_raw_mode() {struct termios raw;cfmakeraw(&raw);tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}

使用其他语言

使用其他语言就要用到 FFI 了,比如下面使用 Rust ,好在 Rust 提供了一个 libc 库可以很容易做到:

fn enable_raw_mode() {unsafe {let mut termios = {std::mem::zeroed()};libc::tcgetattr(libc::STDIN_FILENO, &mut termios);libc::cfmakeraw(&mut termios);libc::tcsetattr(libc::STDIN_FILENO, libc::TCSANOW, &termios);}
}

这只是一个最简单的例子。

下面是纯 FFI 的例子:

#[repr(C)]
#[derive(Copy, Clone)]
struct Termios {c_iflag: u32,c_oflag: u32,c_cflag: u32,c_lflag: u32,cc_c: [u8; 20],c_ispeed: u32,c_ospeed: u32,
}extern "C" {fn tcgetattr(fd: i32, termios: &mut Termios) -> i32;fn tcsetattr(fd: i32, command: i32, termios: &Termios) -> i32;fn cfmakeraw(termios: &mut Termios);
}const STDIN_FILENO: i32 = 0;
const TCSAFLUSH: i32 = 2;fn enable_raw_mode() {unsafe {let mut termios = std::mem::zeroed();cfmakeraw(&mut termios);tcsetattr(STDIN_FILENO, TCSAFLUSH, &mut termios);}
}

via:

  • 终端输入输出的三种模式
    https://guidao.github.io/termio.html

  • 终端的 Raw Mode
    https://erzbir.com/archives/terminal-raw-mode


文章转载自:

http://bbQgcnF6.jfkwp.cn
http://avJI9k1T.jfkwp.cn
http://WIjQqqFr.jfkwp.cn
http://8QTRcLt1.jfkwp.cn
http://lHMOdiut.jfkwp.cn
http://abnZCnuK.jfkwp.cn
http://8BnNiCxZ.jfkwp.cn
http://8HVc1zf8.jfkwp.cn
http://odx98uQY.jfkwp.cn
http://0qvn8UsI.jfkwp.cn
http://lQWnK19O.jfkwp.cn
http://7Z6LPVXU.jfkwp.cn
http://pW1uWaP8.jfkwp.cn
http://qNiwEVRQ.jfkwp.cn
http://vL8jLNQh.jfkwp.cn
http://dAnqLZiU.jfkwp.cn
http://LrOTGlRc.jfkwp.cn
http://MLBYBo4V.jfkwp.cn
http://XchzIfk1.jfkwp.cn
http://2UJWtLAg.jfkwp.cn
http://BNuYB2Yq.jfkwp.cn
http://K8sCwnpL.jfkwp.cn
http://FtJjqFeA.jfkwp.cn
http://UdSJ8DNX.jfkwp.cn
http://amvLk8gE.jfkwp.cn
http://Z95WykEC.jfkwp.cn
http://mQJ4YacV.jfkwp.cn
http://3ml0Ndpz.jfkwp.cn
http://hSrm7v49.jfkwp.cn
http://RkPh1TkI.jfkwp.cn
http://www.dtcms.com/wzjs/711945.html

相关文章:

  • 电子商务网站开发难点网站建设最快多长时间
  • 我的网站百度搜不到陕西建设网综合综合服务中心
  • 建设信用卡银行积分商城网站wordpress 图片选择器
  • 查询网站mx记录受欢迎自适应网站建设地址
  • 福州网站制作公司网页微信手机版
  • 外贸网站wordpress加ssl网站建设招标方式
  • 阿里巴巴国际贸易网站推广工具精品网文
  • 建视频网站需要多大空间军事最新新闻播报
  • 设计公司网站建设费用博客主题Wordpress
  • 做淘客网站哪个cms好网站制作专家
  • 论坛网站模块网站设计抄袭
  • 仿站定制模板建站网站建设公开课
  • 怎么在网站上做签到百度站长seo
  • 长治网站建设费用社交网站的建设现状
  • 百度搜索显示网站logo可信网站认证查询
  • 展览展示展厅设计济南seo全网营销
  • 茌平网站建设公司电视台网站建设
  • 长春免费建站怎样设计一个网页页面
  • 做电影网站要多少钱货源网
  • 政务网站建设信息公司直招的招聘网站
  • 引擎搜索网站模板网站模板制作
  • 怎么找出网站的备案号网站建设与排名
  • 网站建设教程(项目式)123上网之家网址
  • 做论坛网站需要多少钱公众号文案里怎么做网站链接
  • 龙岩市官方网站智能手机软件开发
  • 建设网站的调研报告青岛市黄岛区城市建设局网站
  • 有专门做食品的网站吗重庆在线高校
  • 网站怎么做交易子域名大全查询
  • 海南建设网站公司怎么在Front做网站
  • 个人网站建站指南网页界面设计特点