unix环境高级编程-读书笔记与习题解答-第一篇

时间:2022-07-02 22:54:24

从这周开始逐渐的进入学习状态,每天晚上都会坚持写c程序,并且伴随对这本书的深入,希望能写出更高质量的读书笔记和程序。

本书的第一章,介绍了一些关于unix的基础知识,在这里我不想去讨论linux到底是不是unix或者和unix的关系是什么这样的问题,只针对对自己有价值的知识点进行分析。

第一节 登录

第一节中,作者叙述了unix下用户登录的基本过程,包括保存用户口令的文件 /etc/passwd , 以及该文件的格式,该口令文件中的登录项的组成结构为由7个冒号分隔的字符串,依次是 :

登录名
加密口令
用户的数字ID
用户的数字组ID
注释字段
用户的起始目录
用户的shell程序
第二节 文件和目录

该节的重点是一个目录操作 api 和 unix 目录的组成以及目录的各种属性的说明,另外也说明了unix中组成目录的可用字符,目录操作api,是入门这本教程中的第一个 api, 也是很常用的一个 api, 在笔记的末尾我会综合讨论本章节所使用的所有 api 的细节。

第三节 输入和输出

程序就是一个算法,算法的操作资料来源于输入,计算完毕之后就必须输出结果给人类才可以,所以输入输出是很重要的基础内容。

unix系统使用文件描述符进行文件操作,类似windows中的句柄(至今不明白翻译成句柄是不是为了更加符合中国人的思维),是一个整数,内核通过这个描述符,进行对文件的各种操作,这个描述符对程序员也很重要,我们在得到这个描述符之后,才可以对文件进行相应的操作。

三个标准文件描述符, 即标准输入,标准输出和错误输出,默认都是终端,由shell默认指配这3个文件描述符,比如 ls 这样的命令, 如果想重定向输出的话, 书中给出了一个简单例子, 就是 使用 > 号, 比如

 ls -al > dir.txt

这样就很简单的把 ls -al 的输出指向到了一个文件里。

接下来书中讲述了基本的io操作api, 并且提供了示例代码,该示例可以复制任意unix文件, 该程序的分析以及api介绍,会在笔记末尾部分。

================================================

以上章节中所使用的 unix api 解析
头文件
#include <dirent.h>
#include <sys/types.h>
opendir 原型
DIR * opendir(const char * path);
该api 打开一个目录, 打开失败的时候返回 NULL, 目录是文件也会返回NULL, 打开成功则返回一个DIR指针 closedir 原型
int closedir(DIR * dir_handle);
closedir()
关闭参数dir所指的目录流。关闭成功则返回0,失败返回-1,错误原因存于errno 中。EBADF 参数dir为无效的目录流。
注意:目录文件作为一种文件,在打开必须关闭,否则会由于文件的进程打开文件过多而不能打开新的文件。因此opendir函数和closedir函数同样是配对出现的。 DIR 结构体原型 typedef struct __dirstream DIR;
struct __dirstream
{
void *__fd; /* `struct hurd_fd' pointer for descriptor. */
char *__data; /* Directory block. */
int __entry_data; /* Entry number `__data' corresponds to. */
char *__ptr; /* Current pointer into the block. */
int __entry_ptr; /* Entry number `__ptr' corresponds to. */
size_t __allocation; /* Space allocated for the block. */
size_t __size; /* Total valid data in the block. */
__libc_lock_define (, __lock) /* Mutex lock for this structure. */
}; DIRENT 结构体原型
struct dirent
{
long d_ino; /* inode number */
off_t d_off; /* offset to this dirent */
unsigned short d_reclen; /* length of this d_name */
unsigned char d_type; /* the type of d_name */
char d_name [NAME_MAX+1]; /* file name (null-terminated) */
}

书中的示例程序

#include <sys/types.h>
#include <dirent.h>
int
main(int argc, char ** argv)
{
DIR * dir;
struct dirent * dirt;
if(argc != 2)
{
printf(" you need input a dir name");
}
if((dp = opendir(argv[1])) == NULL)
{
printf("can not open %s", argv[1]);
}
while((dirt = readdir(dir)) != NULL)
{
printf("%s\n", dirt->name);
}
closedir(dir);
exit(0)
}

该示例程序是我改写过了的,没由调用书中的头文件,因为我想构造最短小精悍的可以执行的示例程序,程序非常简单,强行分析的话这个程序是漏洞百出的,不过既然是例子就是越简单越能说明问题越好,该程序从终端获取要去读取的目录,打开成功的话就循环读取目录中的文件并打印出来,非常显而易见的 api 调用方法。

头文件
#include <unistd.h>
api 原型
size_t read(int fd, void *buf, size_t count);
返回值:成功返回读取的字节数,出错返回-1并设置errno,如果在调read之前已到达文件末尾,则这次read返回0 #include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
返回值:成功返回写入的字节数,出错返回-1并设置errno

书中的示例程序

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define BUFFSIZE 8192
int
main(void)
{
int n;
char buf[BUFFSIZE];
while((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0)
{
if(write(STDOUT_FILENO, buf, n) != n)
{
printf("write errors");
}
}
if(n < 0)
{
printf("read error");
}
return 0;
}

当你执行该程序生成的二进制文件的时候,发现你输出什么,它就输出什么,该程序循环调用read 然后 直接write 在while中,可以一直循环读写你输入的内容, 所以该程序可以复制任何unix文件。

gcc -o test ./test.c
cat test.c > ./test > test1.c 你就会发现你的程序被复制到了test1.c中, 该程序并不是让你把所有的文件打在终端里才会复制,书中的意思是要你使用管道来操作。

决定深入的学习linux编程,所以这周开始学习 unix高级环境编程 这本著作, 做服务器端程序设计的程序员,都需要去学习操作系统, 一个服务是否稳定,是否高效,涉及到很多因素,而操作系统是服务运行的环境,各种系统调用,进程通信,IO模型,socket通信,等等这些都是基础,不精通这些东西是没有办法深入服务器端程序设计的,所以我决定认真学习这本书,同时彻彻底底的把django这个框架分析一遍。

这是开头,书中的第一章的内容也很简单,所以这篇笔记主要是说学习计划的,如何有效的学习这本书,或者说适合我的这本书的学习计划,应该有一个大概的规划,我已经经历过无数次无头苍蝇般的学习经历了,这么多年,我觉得应该去变化一下方式了,或者是在方法上面动一下脑筋。

回忆自己以前的学习和工作经历,我的觉悟是,积累是非常重要的,所以更要坚持做笔记,写博客,对于这本书的学习,在一边读的同时,我已经在实践书中的例子程序了,而且我从来不照抄程序,而是完全理解透彻之后,自己查找相应的api,写一个类似书中的程序出来,这样我才能完全理解那些api。