Unix/Linux编程实践教程(二)

在本章中,我们将会学习到:
1.联机帮助的作用与使用方法
2.Unix的文件操作函数:open、read、write、lseek、close
3.文件的建立与读写
4.文件描述符
5.缓冲:用户级的的缓冲和系统级的缓冲
6.内核模式、用户模式和系统调用的代价
7.Unix表示时间的方法与时间格式间的转换
8.借助utmp文件来列出已登陆的用户
9.系统调用中的错误检测与处理


之前忘记提了,程序与程序员之间的通讯是经由内核才能通信的,因为
在内核的上下是两片天地,下是硬件,对于人来说是比较乱的,
上是对于人的操作,我们要看着相对舒服的。

该章中还是以,
1.有什么功能
2.怎么实现的
3.我们怎么实现

为线索
所以,我们以 who为例:
1.联机帮助:
书上说,name(1)是表示小节编号1,man中有很多节,1为用户命令的帮助,2是关于系统的
调用帮助,5是关于配置问及那的帮助, 但是还是不太理解
NAME             Name of the command
SYNOPSIS         General usage parameters of the command.
DESCRIPTION     Generally describes of the command and what it does
OPTIONS         Describes all the arguments or options to the command
SEE ALSO         Lists other commands that are directly related to the command in the man page or closely resembling its functionality.
BUGS             Explains any known issues or bugs that exist with the command or its output
EXAMPLES         Common usage examples that give the reader an idea of how the command can be used.
AUTHORS         The author of the man page/command.


ok,我们已经用了man查看了who的作用和使用方法,那么who是如何实现的这个功能的呢?
查看登陆的用户呃,是不是觉得很高级的东西呢?我们一起来看看吧。
我们一开始学习,应该要学会查看资料,那怎么查找呢?就在本机上查找!
我们要做到:
1.阅读联机帮助
2.搜索联机帮助
3.阅读.h文件
4.从参阅部分(SEE ALSO)得到启示

例子:
man who                //查看他的帮助文档
If FILE is not specified, use /var/run/utmp.  /var/log/wtmp as FILE is common.  
If ARG1 ARG2 given, -m presumed: 'am i' or 'mom likes' are usual.
大概的意思应该是:如果who后面没有指定的FILE(图中的SYNOPSIS中有),那么默认是/var/run/utmp,/var/log/wtmp
                  给予两个词的量的话,可以用'am i' or 'mom likes'。。。。强行翻译。。。。
然后,我们就可以知道了who是从utmp获取的信息,那么我们读取utmp中的数据就可以达到相同的效果
我们可以使用man指令查看有关utmp的一些指令,可以使用man -k utmp
这里解释一下man -k 的作用:
man -k printf
          Search the short descriptions and manual page names for the keyword printf as regular expression.  Print out any matches.  Equivalent to apropos printf.
也就是搜索出在man中descriptions中和name中有printf关键词作为正则表达式打印出来。
但是我发现Ubuntu和CentOS是不一样的,而Ubuntu上和书上的类似,但是书上说的是utmp(4)
我的ubuntu服务器上是utmp(5),我不知道这是啥意思,先放着,看看看到后面能不能理清楚,
不能的话只能请教大佬了。(question1)


ok,我们继续撸。我使用命令vim /usr/include/utmp.h里面看不到结构体,不知道为啥
使用man utmp的命令后却可以看到他定义的一些结构体,不知道为什么。。。。(question2)


好吧,跟着书做了那么久的准备工作,我们明确,我们需要的是我们可以自行靠着现有的资料
自己写出程序来,而不是一直靠着抄别人的代码。
ok,开始写自己的who把:
1.从文件中读取数据结构
2.将结构中的信息以合适的形式显示出来


1.要解决问题1,我们需要的工具是读取数据结构的函数,那什么能胜任呢?
getc?fgets?使用getc逐个字节读取,这样太繁琐了,而且效率极低。我们怎么寻找我们
所需的函数呢?书中提供的方法是: 使用联机帮助寻找答案,即man -k寻找,这时我们目的
是需要读取文本中的字符的,那么关键字就是 readfile,那么命令为:
man -k file可是-k只能提供一个关键字的寻找,那怎么办呢?我们可以并列使用grep命令
man grep的解释为:
GREP(1)                          General Commands Manual                                                                          GREP(1)
NAME
       grep, egrep, fgrep, rgrep - print lines matching a pattern

SYNOPSIS
       grep [OPTIONS] PATTERN [FILE...]
       grep [OPTIONS] [-e PATTERN]...  [-f FILE]...  [FILE...]

DESCRIPTION
       grep  searches  the named input FILEs for lines containing a match to the given PATTERN.  If no files are specified, or if the file “-” is given, grep searches standard input.  By
       default, grep prints the matching lines.

       In addition, the variant programs egrep, fgrep and rgrep are the same as grep -E, grep -F, and grep -r, respectively.  These variants are deprecated, but are provided for backward compatibility.
就是寻找字符串,并标出,那么组合使用即为man -k file | grep read。就是使用man找出describtion或者name中带有关键词file的,列出,然后在其中筛选出其中字符串中带有read作为最终的输出。
啊,书上还说了, openclose,对系统文件调用所必须的,但是我不知道从那里得知的。
1.open:
功能: Given  a pathname for a file, open() returns a file descriptor
头文件:       #include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>
函数原型:
int open(const char *pathname, int flags);
参数:
pathname:文件名
flags:对文件操作的类型,O_RDONLY只读、O_WRONLY只写、O_RDWR可读写
返回值:
-1:遇到错误
正整数:文件描述符
那么问题来了,一个进程对一个文件进行访问时(读、写?),另一个进程可以对他进行访问吗?
Unix中是可以的(这句话是书上在04年?说的?貌似是很久之前了,还未查证现在如何)
因为一个文件被打开后,会建立一个进程和内核的一个链接,称为文件描述符,这个文件描述符
是唯一标识这个链接的,也即是如果多个进程对其进行访问,就会有不同的文件描述符。
2.read
功能:read - read from a file descriptor
头文件:
#include <unistd.h>
函数原型:
ssize_t read(int fd, void *buf, size_t count);
参数:
int fd:函数描述符
void *buf:读取字符后存储的指针
size_t count:读取的长度
返回值;
On success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number.  It is not an error if this number is smaller than
the number of bytes requested;
On error, -1 is returned, and errno is set appropriately.
3.close
功能:
close - close a file descriptor
头文件:
#include <unistd.h>
函数原型:
int close(int fd);
参数:
int fd :文件描述符
返回值:
close() returns zero on success.  On error, -1 is returned, and errno is set appropriately.
好了,介绍完了
我们来看看我们需要写的程序who的一个流程
打开文件-----》 读取文件-------》显示数据---------》关闭文件
         struct utmp {
               short   ut_type;              /* Type of record */
               pid_t   ut_pid;               /* PID of login process */
               char    ut_line[UT_LINESIZE]; /* Device name of tty - "/dev/" */
               char    ut_id[4];             /* Terminal name suffix,or inittab(5) ID */
               char    ut_user[UT_NAMESIZE]; /* Username */
               char    ut_host[UT_HOSTSIZE]; /* Hostname for remote login, or kernel version for run-level messages */
               struct  exit_status ut_exit;  /* Exit status of a process marked as DEAD_PROCESS;
哎,不知道为什么得出的结果总是有warming。
who2.c:44:21: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
const time_t *ch = &(utmpbuf->ut_time);
说我的类型是错误的,可是我找不到我的ut_time是啥类型的呃,再man utmp中没有搜到ut_time的类型定义,也不知道怎么啥类型,虽然报有warming但是还是能用,但是现在还有一个问题就是系统中自带的who得出的格式是和我写出来的格式是不一样的,那么问题在于两个方面吧:
1.我的转换函数用错了
2.我的结构体内的变量用错了,不能用这个ut_time,但是我也不能找道其他的了呃
只能先放着了,用的时间已经很长了。

更正:
好吧,我已经解决了那个warming的问题,但是我还是不知道ut_time是什么类型的,可是可以进行
强制类型转换把ut_time转换成long就可以把那个warming给去掉了,关键是我好像查到ctime这个
函数的类型是char *ctime(const time_t *timep);
找不到time_t的变量类型呃。。。。
/*who1.c -a first vertion01 of the who program
*                       open,read UTMP file,and show results
*/
#include <stdlib.h>
#include <stdio.h>
#include <utmp.h>
#include <fcntl.h>
#include <unistd.h>

#define SHOWHOST                /*include remote machine on output*/

void show_info(struct utmp *);
int main()
{
        printf("qq");
        struct utmp current_record;     //read info into here
        int utmpfd;                     //read from this description

        if(utmpfd = open(UTMP_FILE,O_RDONLY) == -1)
        {
                printf("qs");
        struct utmp current_record;     //read info into here
        int utmpfd;                     //read from this description

        if(utmpfd = open(UTMP_FILE,O_RDONLY) == -1)		//运算优先级是判断open == -1,不等则返回false即-1,再执行赋值语句,utmpfd=-1
        {
                printf("qs");
                exit(-1);
        }
        printf("%d",utmpfd);
        while(read(utmpfd,¤t_record,reclen) == reclen)
        {
                printf("sss\n");
                show_info(¤t_record);
        }
        close(utmpfd);
        return 0;

}
void show_info(struct utmp *utmpbuf)
{
        printf("%-8.8s",utmpbuf->ut_user);      //the logname
        printf(" ");                            //a space
        printf("%-8.8s",utmpbuf->ut_line);      //the tty
        printf(" ");                            //a space
//        printf("%10ld",utmpbuf->ut_time);     //login time
        printf(" ");                            //a space
        #ifdef SHOWHOST
//              printf("(%s)",utmpbuf->ut_host);        //the host
        #endif
                printf("\n");
}

//bug:vertion0.1
//it can't display time 
//its empty login is redundance


cp篇:
功能:cp - copy files and directories
复制一个文件或者文件夹,如果该文件或者文件夹不存在的话就创建一个。
所以涉及到一个创建文件或者文件夹,一个写数据。

所以,


creat
功能:创建/重写一个文件
头文件:#include <fcntl.h>
函数原型:int creat(const char *pathname, mode_t mode);
参数
char *pathname:创建一个名为pathname的文件
mode_t :该文件的读写权限
返回值:-1错误 fd成功创建
creat告诉内核创建一个名为pathname的文件,如果那个文件不存在,就创建他
,如果已经存在就清空他,并把文件的长度设为0。
fd为一个指向addressbook的文件描述符。。。。不懂

write:
功能:write - write to a file descriptor
写数据向一个文件描述符
头文件:
#include <unistd.h>
函数原型:
ssize_t write(int fd, const void *buf, size_t count);
参数:
int fd:文件描述符
void *buf:要写入的字符缓冲区首地址
size_t count:写入的长度
返回值:
-1错误
num written 成功写入的长度
/*cp version0.1 for two patamaters ,one is source file the other is targe file


*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>

#define buflen 4096

int main(int ac,char *av[])
{
	int source_fd,target_fd,char_num;
	char buf[buflen];
	if(ac != 3)
	{
		fprintf(stderr,"usage:%s source destination\n",*av);
		exit(-1);
	}
	
	//read the source
	if((source_fd = open(av[1],O_RDONLY)) == -1)	//false read
		oops("can not open",av[1]);		//oop is what??
	
	if((target_fd = creat(av[2],O_WTONLY)) == -1)	//false creat
		oops("can not  creat",av[2]);
	
	//read the data to buf
	while((char_num = read(source_fd,buf,buflen))>0) //char_num is real length
	{
		if(write(targe_fd,buf,char_num) != char_num)	//if yes show that reading and writing donot match
		{
			oops("write error to",av[2]);
		}
	}
	
	if(char_num == -1)
		oops("error closing files",av[1]);
	
	if(close(source_fd) == -1 || close(target_fd) == -1)
		oops("error closing files","");
}

void oops(char* s1,char* s2)
{
	fprintf(stderr,"Error:%s",s1);
	perror(s2);
	exit(1);
}


ok,这就结束了,是不是感觉很简单呢?当然了,毕竟是跟着书一起学的,如果让你自己写的话是很难的
而且现在还有很多东西还没有涉及呢!

提高文件I/O效率的方法:使用缓冲

什么是缓冲?我们在写程序的时候使用的很多了吧,那么他的物理内涵是什么呢?

如图,我们可以得知,进程和文件的通信是要经过内核的,即read和write是由内核执行的,执行权会从用户转移到内核代码,执行内核是需要时间的,所以当我们频繁使用内核代码的时候会需要很多时间进行权限的转换(用户代码转换到内核的过程中,一些特殊的堆栈的和环境的建立必须配置好,这耗费了时间),如果我们可以减少频繁调用内核代码的次数就可以减少我这个时间。而很多一些外设和一些内存空间必须经过内核才可以访问,所以进入内核是必须的,但是,在合理的时机进入就显得尤为重要了。

就比方说,我今天的行程,我得要思考我今天要带什么东西出去,因为我的书包是有限的,不想
频繁地回寝室放拿东西,回一趟寝室和去图书馆很耗时间,我怎么合理的安排才能尽可能的减少

这个不必要的时间呢?差不多就这个意思。

所以,我们要增加一次的缓存量,就是书包要大点,但是太大背着又太累,哎呀,班长要我回寝室
一趟交作业,那这个是必须要回去一趟了,所以我就可以缓冲一下,不用拿那么多东西。理解理解,

差不多,emmmmm。。。。

但是我们的程序里的访问内核的次数开销就可以是我们控制的了,所以我们写程序尽可能地减少
内核的访问次数。

内核缓冲机制:

内核中也采用了这个缓冲的方法,怎么做的呢?


在内核中安排有许多的内存块作为缓冲,进程需要磁盘中的某个数据时,先看看内核缓冲区中有没有,如果有就直接复制到进程中,这样就不用申请内核的访问权和执行权了,如果没有的话,此时内核还没有鸟他,先加入申请队列,然后把这个进程挂起,执行别的进程,一段时间(时间很短)后,内核把响应的数据块从磁盘中读到内核缓冲区(是不是多个进程的任务一起读取呢?),再把数据复制到进程缓冲区,再唤醒进程,更好地利用了时间。应该就是顺带的意思吧,emmmmm。


处理系统调用中的错误

对于不同的错误要做出相应的操作,给出相应的提示。你可以根据错误的返回值,在errno,h上有定义,
不过我没有查到具体的值。。。
还有就是可以使用perror这个函数系统可以帮你寻找错误原因,也就是封装我们如果要自己写的话所写的
判断函数,一样的。


-------------------------后记

这个花的时间有点长了,但是还是有点搞不明白,但是也只能先放过了。。。有啥不对的,或者需要指点的请一定不要吝啬,谢谢了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值