第一章 UNIX基础知识
UNIX文件系统是目录和文件组成的一种层次结构,目录的起点称为根(root),其名字是一个字符/。
文件属性是指文件类型(是普通文件还是目录)、文件大小、文件所有者、文件权限以及文件最后的修改时间等。Stat和fstat函数返回包含所有文件属性的一个信息结构。
struct stat { unsigned long st_dev; /* Device. */ unsigned long st_ino; /* File serial number. */ unsigned int st_mode; /* File mode. */ unsigned int st_nlink; /* Link count. */ unsigned int st_uid; /* User ID of the file's owner. */ unsigned int st_gid; /* Group ID of the file's group. */ unsigned long st_rdev; /* Device number, if device. */ unsigned long __pad1; long st_size; /* Size of file, in bytes. */ int st_blksize; /* Optimal block size for I/O. */ int __pad2; long st_blocks; /* Number 512-byte blocks allocated. */ long st_atime; /* Time of last access. */ unsigned long st_atime_nsec; long st_mtime; /* Time of last modification. */ unsigned long st_mtime_nsec; long st_ctime; /* Time of last status change. */ unsigned long st_ctime_nsec; unsigned int __unused4; unsigned int __unused5; };
创建新目录时会自动创建两个文件名:.(称为点)和..(称为点点),点指当前目录,点点指父目录,在最高层次的根目录中,点和点点相同。
以斜线开头的路径名称为绝对路径名,否则称为相对路径名。
自己实现ls命令
//myls.c #include "apue.h" #include <dirent.h> int main( int argc,char *argv[] ) { DIR *dp; struct dirent *dirp; if ( argc != 2 ) //printf("usage: myls directory_name"); err_quit("usage: myls directory_name"); if ( (dp = opendir(argv[1])) == NULL ) printf("can't open %s",argv[1]); while ( (dirp = readdir(dp)) != NULL ) printf("%s\n",dirp->d_name); closedir(dp); exit(0); }
在shell中运行gcc myls.c -o myls
运行结果:
myls没有把.和..过滤掉,而系统的ls把.和..过滤掉了,ls命令在打印目录项前一般按照字母顺序将名字排序。
每个进程都有一个工作目录,有时也称其为当前工作目录,所有相对路径名都从工作目录开始解释的,chdir函数更改其工作目录。
文件描述符通常是一个小的非负整数,内核用它标识一个特定进程正在访问的文件。当内核打开一个已有文件或者创建一个新文件时,它返回一个文件描述符。
函数open、read、write、lseek以及close提供了不用缓冲的I/O。
程序是存放在磁盘上、处于某个目录中的一个可执行文件;程序的执行实例被称为进程。Unix系统确保每个进程都有一个唯一的数字标识符,被称为进程ID,进程ID总是一个非负整数。
//mygetpid.c //获得进程ID号 #include "apue.h" int main(void) { printf("hello world form process ID %d\n",getpid()); exit(0); }
有三个用于进程控制的主要函数:fork、exec和waitpid。
自行简化实现一个简单的shell程序
#include "apue.h" #include <sys/wait.h> int main(void) { char buf[MAXLINE];//form "apue.h" pid_t pid; int status; printf("%% ");//print '%' while ( fgets(buf,MAXLINE,stdin) != NULL ) { if ( buf[strlen(buf) - 1] == '\n') buf[strlen(buf) - 1] = 0; if ( (pid = fork()) < 0) { printf("fork error"); //err_sys("fork error"); } else if (pid == 0) {//child execlp(buf,buf,(char *)0); printf("couldn't execute:%s",buf); //err_ret("couldn't execute:%s,buf"); exit(127); } //parent if ((pid = waitpid(pid,&status,0)) < 0) printf("waitpid error");//err_sys("waitpid error"); printf("%% ");//print '%' } exit(0); }运行结果:
说明:
>>fgets返回的每一行都以换行符终止,后随一个null字节,故用标准C函数strlen计算字符串长度,然后用以个null字节替换换行符,因为execlp函数要求参数以null而不是以换行符结束;
>>调用fork()创建一个新进程。新进程是调用进程的复制品,我们称调用进程为父进程,新创建的进程为子进程。fork向父进程返回新子进程的进程ID,对子进程则返回0。因为fork创建一个新进程,所以说它被调用一次(由父进程),但是返回两次。
>>在子进程中,调用execlp以执行从标准输入读入的命令。
>>子进程调用execlp执行新程序文件,而父进程希望等待子进程终止,这一要求由调用waitpid实现,其参数指定要等待的进程。
当UNIX函数出错时,常常返回一个负值,而且整形变量errno通常呗设置含有附加信息的一个值。文件<errno.h>中定义了符号errno以及可以赋予它的各种常量,这些常量都是以E开头。
口令文件登录项中的用户ID是个数值,它向系统标识各个不同的用户。用户ID为0的用户为根用户或者超级用户,通过getuid()获得。
//mygetpid.c
#include "apue.h"
int main(void)
{
printf("hello world form process ID %d\n",getpid());
exit(0);
}
口令文件登录项也包括用户的组ID,它是一个数值,通过getgid()获得。
//mygetuid.c
#include "apue.h"
int main(void)
{
printf("uid = %d,gid = %d\n",getuid(),getgid());
exit(0);
}
运行结果:
信号是通知进程已发生某种情况的一种技术。终端键盘上有两种产生信号的方法,分别是中断键(Delete或Ctrl+c)和退出键(Ctrl+\)。
长期以来,UNIX系统一直使用两种不同的时间值。
该值是自1970年1月1日00:00:00以来国际标准时间(UTC)所经过的秒数积累计值。基本数据类型是time_t。
这也被称为CPU时间,用以度量进程使用的中央处理机资源。基本数据类型是clock_t。
各种版本的UNIX实现都提供定义明确、数量有限、可直接进入内核的入口点,这些入口点被称为系统调用。虽然有些函数可能会调用一个或多个内核的系统调用,但是它们不是内核的入口点。例如,printf函数会调用write系统调用以输出一个字符串,但是函数strcpy和atoi并不会使用任何系统调用。
==============================================================================
最近在读 Richard Stevens 的大作《UNIX环境高级编程》,相信很多初读此书的人都会与我一样遇到这个问题,编译书中的程序实例时会出现问题,提示 “错误:apue.h:没有那个文件或目录”。
apue.h 是作者自定义的一个头文件,并不是Unix/Linux系统自带的,此头文件包括了Unix程序所需的常用头文件及作者Richard自己写的出错处理函数。所以在默认情况下,gcc在编译时是读不到这个头文件的。
先在这个网站 http://www.apuebook.com/src.tar.gz 下载tar.gz格式的源码包,然后解压至某个目录,比如说/home/godsoul/下,然后进入目录apue.2e,把文件 Make.defines.linux 中的 WKDIR=/home/xxx/apue.2e 修改为 WKDIR=/home/godsoul/apue.2e ,然后再进入apue.2e目录下的std目录,打开linux.mk,将里面的nawk全部替换为awk,如果是用的vi/vim编辑器,可以使用这个 命令 :1.$s/nawk/awk/g (注意前面有冒号)
然后在此目录下运行make命令,即回到 /home/godsoul/apue.2e 目录在终端中输入 “./make” (不含引号)
然后把 /home/godsoul/apue.2e/inlcude 目录下的 apue.h 文件和位于 /home/godsoul/apue.2e/lib 目录下的 error.c 文件都复制到 /usr/include 目录下,apue.2e/lib/libapue.a 到/usr/lib/和 /usr/lib64下。注意复制这文件你需要有root权限。之所以要这样做,是因为gcc在链接头文件时会到 /usr/include 这个目录下寻找需要的头文件,若找不到则报错。
最终还要编辑一下复制过来的 apue.h 文件
在最后一行 #endif 前面添加一行 #include “error.c”
然后进入apue.2e/std 目录,编辑linux.mk。修改里面所有的nawk为awk。
这样就不会报错了。
====================================================================== ========