首页 > 学院 > 操作系统 > 正文

终端I/O之终端标识

2024-06-28 13:27:42
字体:
来源:转载
供稿:网友
终端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.CUOXin.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.


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表