终端I/O之终端标识
历史沿袭至今,在大多数UNIX系统中,控制终端的名字是/dev/tty. POSIX.1提供了一个运行时函数,可被用来确定控制终端的名字。
#include <stdio.h>
char *ctermid(char *ptr);
返回值:若成功则返回指向控制终端名的指针,若出错则返回指向空字符串的指针
如果ptr非空,则它被认为是一个指针,指向长度至少为L_ctermid字节的数组,进程的控制终端名存放在该数组中。常量L_ctermid定义在<stdio.h>中。若ptr是一个空指针,则该函数为数组(通常作为静态变量)分配空间。同样,进程的控制终端名存放在该数组中。
在这两种情况中,该数组的起始地址被作为函数值返回。因为大多数UNIX系统都使用/dev/tty作为控制终端名,所以此函数的主要作用是帮助提高向其他操作系统的可移植性。
实例:ctermid函数
程序清单18-3 POSIX.1 ctermid函数的实现
#include <stdio.h>
#include <string.h>
static char ctermid_name[L_ctermid];
char *
ctermid(char *str)
{
if(str == NULL)
str = ctermid_name;
return(strcpy(str, "/dev/tty")); /* strcpy() returns str */
}
注意,因为我们无法确定调用者缓冲区的大小,所以也就不能防止过度使用该缓冲区。
另外两个与终端标识有关的函数是isatty和ttyname。前者在文件描述符引用一个终端设备时返回真,而后者则返回在该文件描述符上打开的终端设备的路径名。
#include <unisd.h>
int isatty(int filedes);
返回值:若为终端设备则返回1(真),反则返回0(假)
char *ttyname(int filedes);
返回值:指向终端路径名的指针,若出错则返回NULL
实例:isatty函数
程序清单18-4 POSIX.1 isatty函数的实现
#include <termios.h>
int
isatty(int fd)
{
struct termios ts;
return(tcgetattr(fd, &ts) != -1); /* true if no error (is a tty) */
}
程序清单18-5测试isatty函数
#include "apue.h"
int
main(void)
{
printf("fd 0: %s\n", isatty(0) ? "tty" : "not a tty");
printf("fd 1: %s\n", isatty(1) ? "tty" : "not a tty");
printf("fd 2: %s\n", isatty(2) ? "tty" : "not a tty");
exit(0);
}
运行程序清单18-5中的程序时,我们可以得到下面的结果:
实例:ttyname函数
程序清单18-6 POSIX.1 ttyname函数的实现
#include <sys/stat.h>
#include <dirent.h>
#include <limits.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
struct devdir {
struct devdir *d_next;
char *d_name;
};
static struct devdir *head;
static struct devdir *tail;
static char pathname[_POSIX_PATH_MAX + 1];
static void
add(char *dirname)
{
struct devdir *ddp;
int len;
len = strlen(dirname);
/*
* Skip ., .., and /dev/fd.
*/
if((dirname[len - 1] == '.') && (dirname[len - 2] == '/' ||
(dirname[len - 2] == '.' && dirname[len-3] == '/')))
return;
if(strcmp(dirname, "dev/fd") == 0)
return;
ddp = malloc(sizeof(struct devdir));
if(ddp == NULL)
return;
ddp->d_name = strdup(dirname);
if(ddp->d_name == NULL)
{
free(ddp);
return;
}
ddp->d_next = NULL;
if(tail == NULL)
{
head = ddp;
tail = ddp;
}
else
{
tail->d_next = ddp;
tail = ddp;
}
}
static void
cleanup(void)
{
struct devdir *ddp, *nddp;
ddp = head;
while(ddp != NULL)
{
nddp = ddp->d_next;
free(ddp->d_name);
free(ddp);
ddp = nddp;
}
head = NULL;
tail = NULL;
}
static char *
searchdir(char *dirname, struct stat *fdstatp)
{
struct stat devstat;
DIR *dp;
int devlen;
struct dirent *dirp;
strcpy(pathname, dirname);
if((dp = opendir(dirname)) == NULL)
return(NULL);
strcat(pathname, "/");
devlen = strlen(pathname);
while((dirp = readdir(dp)) != NULL)
{
strncpy(pathname + devlen, dirp->d_name,
_POSIX_PATH_MAX - devlen);
/*
* Skip aliases.
*/
if(strcmp(pathname, "/dev/stdin") == 0 ||
strcmp(pathname, "/dev/stdout") == 0 ||
strcmp(pathname, "/dev/stderr") == 0)
continue;
if(stat(pathname, &devstat) < 0)
continue;
if(S_ISDIR(devstat.st_mode))
{
add(pathname);
continue;
}
if(devstat.st_ino == fdstatp->st_ino &&
devstat.st_dev == fdstatp->st_dev) /* found a match */
{
closedir(dp);
return(pathname);
}
}
closedir(dp);
return(NULL);
}
char *
ttyname(int fd)
{
struct stat fdstat;
struct devdir *ddp;
char *rval;
if(isatty(fd) == 0)
return(NULL);
if(fstat(fd, &fdstat) < 0)
return(NULL);
if(S_ISCHR(fdstat.st_mode) == 0)
return(NULL);
rval = searchdir("/dev", &fdstat);
if(rval == NULL)
{
for(ddp = head; ddp != NULL; ddp = ddp->d_next)
if((rval = searchdir(ddp->d_name, &fdstat)) != NULL)
break;
}
cleanup();
return(rval);
}
此处用到的方法是读/dev目录,寻找具有相同设备号和i节点编号的表项。每个文件系统有一个唯一的设备号(stat结构中的st_dev字段http://www.cnblogs.com/nufangrensheng/p/3501385.html),文件系统中的每个目录项有一个唯一的i节点号(stat结构中的st_ino字段)。在此函数中假定当找到一个匹配的设备号和匹配的i节点号时,就找到了所希望的目录项。
我们的终端名可能在/dev的子目录中。于是,需要搜索在/dev之下的整个文件系统子树。我们跳过了很多产生不正确或奇怪结果的目录,它们是/dev/.,/dev/..和/dev/fd。我么也跳过了一些别名,即/dev/stdin、/dev/stdout以及/dev/stderr,它们是对在/dev/fd目录中文件的符号链接。
程序清单18-7 测试ttyname函数
#include "apue.h"
int
main(void)
{
char *name;
if(isatty(0))
{
name = ttyname(0);
if(name == NULL)
name = "undefined";
}
else
{
name = "not a tty";
}
printf("fd 0: %s\n", name);
if(isatty(1))
{
name = ttyname(1);
if(name == NULL)
name = "undefined";
}
else
{
name = "not a tty";
}
printf("fd 1: %s\n", name);
if(isatty(2))
{
name = ttyname(2);
if(name == NULL)
name = "undefined";
}
else
{
name = "not a tty";
}
printf("fd 2: %s\n", name);
exit(0);
}
运行该程序得到:
文件描述符0、1和2都指向了同一终端/dev/tty1.
终端I/O之终端标识的更多相关文章
- CentOS桌面环境如何打开终端以及如何将终端加入右键
安装完CentOS的桌面环境后,默认在桌面以及右键是没有打开终端选项的,要想打开终端,可以由以下步骤: 在左上角菜单[Applications]--->[System Tools]---> ...
- linux tty终端个 pts伪终端 telnetd伪终端
转:http://blog.sina.com.cn/s/blog_735da7ae0102v2p7.html 终端tty.虚拟控制台.FrameBuffer的切换过程详解 Framebuffer Dr ...
- mac使用终端运行mysql,mysql终端,mysql mac,mysql目录,mysql路径
首先去官网下载: http://www.mysql.com/downloads/ 我下载了5.6.11的dmg然后安装,安装完成之后..如果要用终端去玩SQL.那么一开始要输入很长的:/usr/loc ...
- 终端I/O之终端窗口的大小
大多数UNIX系统都提供了一种功能,可以对当前终端窗口的大小进行跟踪,在窗口大小发生变化时,使内核通知前台进程组.内核为每个终端和伪终端保存一个winsize结构: Struct winsize { ...
- Linux6.X图形界面如何打开终端以及如何将终端加入右键
今天刚安装了一个centos 6.9图形界面的系统,安装完成后,鼠标右击没有打开终端的按钮,在网上查了一些资料,搞明白了,分享给大家. 在左上角菜单[Applications]--->[Syst ...
- [Linux]在终端启动程序关闭终端不退出的方法
一般情况下关闭终端时,那么在这个终端中启动的后台程序也会终止,要使终端关闭后,后台程序保持执行,使用这个指令: nohup 命令 & 如:nohup test.sh & 回车,然后提示 ...
- Linux在终端启动程序关闭终端不退出的方法
一般情况下关闭终端时,那么在这个终端中启动的后台程序也会终止,要使终端关闭后,后台程序保持执行,使用这个指令: nohup 命令 & 如:nohup ./studio.sh & 网上其 ...
- 终端I/O之终端选项标志
http://www.cnblogs.com/nufangrensheng/p/3575752.html 中的表18-1至表18-4中列出的所有选项标志(除屏蔽标志外)都用一位或几位(设置或清除)表示 ...
- tcgetattr函数与tcsetattr函数控制终端
6.4.4 使用tcgetattr函数与tcsetattr函数控制终端 为了便于通过程序来获得和修改终端参数,Linux还提供了tcgetattr函数和tcsetattr函数.tcgetattr用于 ...
随机推荐
- [Android][Audio] audio_policy.conf文件分析
不同的Android产品在音频的设计上通常是存在差异的,而这些差异可以同过Audio的配置文件audio_policy.conf来获得.在Android系统中音频配置文件存放路径有两处,存放地址可以从 ...
- Coffee Script 笔记 1
安装node 虽然官网提供了单文件bin的版本 但是并不知道怎么安装npm 于是乎还是得安装msi (坑 当使用 coffee -w -c . 监视文件改变 即时编译的时候会 提示 Error: T ...
- linux下安装subversion出现libtool: link: only absolute run-paths are allowed
configure时加上 --with-apache-libexecdir=/usr/local/apache
- 数据库 CHECKDB 发现了x个分配错误和 x 个一致性错误
--1.在SQL查询分析器中执行以下语句:(注以下所用的POS为数据库名称,请用户手工改为自己的数据库名) use pos dbcc checkdb --2.查看查询结果,有很多红色字体显示,最后结果 ...
- raspbian重置密码
将树莓派上的SD连到电脑,修改cmdline.txt在文件的的最后添加init=/bin/sh dwc_otg.lpm_enable= console=ttyAMA0, kgdboc=ttyAMA0, ...
- centos下apache安装后无法访问
2013.11.28遇到的问题: -------------------------------------- 一.centos下apache安装后无法访问 得查一下防火墙的问题 iptables添加 ...
- Python输入输出(IO)
程序会有输入和输出,输入可以从标准输入或是从一个文件读入数据,程序的输出可以以一种友好可读的方式(human-readable)打印出来,或是写进一个文件,而标准输入和标准输出(键盘和显示器)在程序的 ...
- 未能加载文件或程序集“Oracle.DataAccess, Version=2.112.1.0, Culture=neutral, PublicKeyToken=89b483f429c47342”或它的某一个依赖项。 解决方法
webconfig文件对于oracle的映射错误.需要在以下位置修改 <runtime> <legacyCorruptedStateExceptionsPolicy enabled= ...
- Spring REST实践之REST基本介绍
REST是什么 REST(REpresentational State Transfer)是一个设计分布式web应用的框架风格,有六个基本原则: Client-Server:应用的参独立与者可分为Cl ...
- 妙用缓存调用链实现JS方法的重载
来自于我的博客http://sweets.cf/,转载注明出处 1.什么是方法重载 方法重载是指在一个类中定义多个同名的方法,但要求每个方法具有不同的参数的类型或参数的个数. 简而言之就是:方法重载就 ...