首页 > 代码库 > APUE学习笔记:第一章 UNUX基础知识

APUE学习笔记:第一章 UNUX基础知识

1.2 UNIX体系结构

从严格意义上,可将操作系统定义为一种软件(内核),它控制计算机硬件资源,提供程序运行环境。内核的接口被称为系统调用。公用函数库构建在系统调用接口之上,应用软件即可使用公用函数库,也可使用系统调用。shell是一种特殊的应用程序,它为运行其他应用程序提供了一个接口

从广义上,操作系统包括了内核和一些其他软件,这些软件使得计算机能够发挥作用,并给予计算机以独有的特性(软件包括系统实用程序,应用软件,shell以及公用函数库等)

1.3  shell

shell是一个命令行解释器,它读取用户输入,然后执行命令。用户通常用终端(交互式shell),有时则通过文件(shell script)向shell进行输入

UNIX系统常见shell:

                        名称                 路径

        Boune shell:       /bin/sh

        Bourne-again shell:  /bin/bash

        C shell:        /bin/csh

        Korn shell:      /bin/ksh

        TENEX C shell:    /bin/tcsh  

C shell其控制流类似于C语言,它支持Bourne shell没有的某些特色功能,例如作业控制、历史记忆机制以及命令行编辑等

Korn shell它与Bourne shell向上兼容,并具有使C shell广泛得到应用的一些特色功能,包括作业控制以及命令行编辑

Bourne-again shell是GNUshell,所有linux都提供这种shell,它被设计成遵循POSIX的,同时也保留了与Bourne shell的兼容性。它支持C shell和Korn shell两者的特色功能

TENEX C shell 是C shell的加强版本。它从TENEX操作系统借鉴了很多特色,例如命令完备。TENEX C shell在C shell的基础上增加了很多特征,常被用来替换C shell

注:linux默认shell是Bourne-again shell.事实上,/bin/sh将链接到/bin/bash.FreeBSD 和Mac OS X的默认用户shell是TENEX C shell,但是因为使用C shell编程语言极其困难,所以它们使用Bourne shell编写用于管理方面的shell脚本

 

1.4文件和目录

1.文件系统

UNIX文件系统是目录和文件组成的一种层次结构,目录的起点称为根(root),其名字是一个字符/。文件属性是指文件类型(是普通文件还是目录)、文件大小、文件所有者、文件权限以及文件最后的修改时间等。

2.文件名

目录中的各个名字称为文件名。不能出现在文件名中的字符只有斜线(/)和空操作符(null)两个。

创建新目录时会自动创建两个文件名:.(当前目录),..(父目录)

3.路径名

以斜线开头的路径名称称为绝对路径名,否则称为相对路径名

实例:1-1 列出一个目录中的所有文件

 1 #include"apue.h" 2 #include<dirent.h> 3 int main(int argc,char *argv[])    //argc表示参数个数;argv表示参数内容 4 { 5     DIR *dp; 6     struct dirent *dirp;       7     if(argc!=2) 8     err_quit("usage:ls directory_name"); 9     if((dp=opendir(argv[1]))==NULL)  //opendir函数返回指向DIR结构的指针10     err_sys("can‘t open%s",argv[1]);11     while((dirp=readdir(dp))!=NULL)  //readdir返回一个指向dirent结构的指针12     printf("%s\n",dirp->d_name);13     closedir(dp);14    exit(0);15 }

这段代码类似于ls命令,不过这段代码只打印一个目录中各个文件的名字,不显示其他信息

编译方法:$ cc xxx.c  编译

              $ ./a.out  运行   

(可能很多初学者在编译这段代码时会出现找不到apue.h,这里我发个配置apue.h头文件的链接 http://www.linuxidc.com/Linux/2013-01/77686.htm)

 

1.5输入与输出

1.文件描述符

文件描述符通常是一个小的非负整数,内核用它标识一个特定进程访问的文件。当内核打开一个已有文件或创建一个新文件时,它返回一个文件描述符。在读写文件时,就可以使用它。

3.不用缓冲的I/O

函数open、read、write、lseek以及close提供了不用缓冲的I/O。这些函数都使用文件描述符

程序清单1_2 将标准输入复制到标准输出

 1 #include"apue.h" 2 #define BUFFSIZE 4096 3 int main(void) 4 { 5     int n; 6     char buf[BUFFSIZE]; 7     while((n=read(STDIN_FILENO,buf,BUFFSIZE))>0) 8         if(write(STDOUT_FILENO,buf,n)!=n) 9             err_sys("write error");10         if(n<0)11         err_sys("read error");12     exit(0);13 }


4.标准I/O

标准I/O函数提供一种对不用缓冲I/O函数的带缓冲的接口。使用标准I/O函数可以无需担心如何选取最佳的缓冲区大小,例如1_2中BUFFSIZE常量的大小。使用标准I/O函数的另一个优点是简化了对输入行的处理。例如,fgets函数读一完整的行,而read函数读指定字节数。(printf函数便是标准I/O函数)

实例:1_3 用标准I/O将标准输入复制到标准输出

 1 #include"apue.h" 2 int main() 3 { 4     int c; 5     while((c=getc(stdin))!=EOF) 6         if(putc(c,stdout)==EOF) 7         err_sys("output error"); 8     if(ferror(stdin)) 9     err_sys("input error");10     exit(0);11 }

附:无缓冲I/O操作和标准I/O操作的区别 http://blog.csdn.net/cowbane/article/details/6630298

 

1.6程序和进程

1.程序是存放在磁盘上,处于某个目录中的一个可执行文件。使用6个exec函数中的一个由内核将程序读入存储器,并使其执行

2.进程和进程ID

程序的执行实例被称为进程。UNIX系统确保每个进程都有一个惟一的数字标识符,称为进程ID,进程ID总是一非负整数

实例:1_4 打印进程ID

1 #include"apue.h"2 int main()3 {4     printf("hello world from process ID %d\n",getpid());5     exit(0);6 }

此程序运行时,它调用函数getpid得到其进程ID

3.进程控制

有三个用于进程控制的主要函数:fork,exec和waitpid

实例: 1_5 从标准输入读命令并执行(类shell程序)

 1 #include"apue.h" 2 #include<sys/wait.h> 3 4  5 int main(void) 6 { 7     char buf[MAXLINE]; 8     pid_t pid;     9     int status;13     printf("%% ");14     while(fgets(buf,MAXLINE,stdin)!=NULL){15     if(buf[strlen(buf)-1]==\n)16     buf[strlen(buf)-1]=0;             //用null替换换行符17     if((pid=fork())<0){            //fork向父进程返回新子进程的进程ID(非负),对子进程则返回0;所以说它被调用一次,返回两次18     err_sys("fork error");19     }20     else if(pid==0){          21     execlp(buf,buf,(char *)0);    //execlp执行从标准输入读入的命令。fork和跟随其后的exec两者的组合是某些操作系统所称的产生(spawn)一个新进程22     err_ret("couldn‘t execute:%s",buf);23     exit(127);24     }25     if((pid=waitpid(pid,&status,0))<0)    //waitpid函数返回子进程的终止状态26     err_sys("waitpid error");27     printf("%% ");28     }29     exit(0);30 }

该程序的主要限制是不能向所执行的命令传递参数,例如不能指定要列表的目录名,只能对工作目录执行ls命令

4.线程和线程ID

在一个进程内的所有线程共享同一地址空间、文件描述符、栈以及进程相关的属性。线程ID只在它所属进程内起作用。一个进程中的线程ID在另一个进程中并无意义。

1.7出错处理

当unix函数出错时,常常返回一个负值,而且整型变量errno通常被设置为含有附加信息的一个值。例如,open函数如成功执行则返回一个非负文件描述符,如出错则返回-1。

实例:1_6 示例strerror和perror两个出错函数的使用方法

1 #include"apue.h"2 #include<errno.h>3 int main(int argc,char *argv[])4 {5     fprintf(stderr,"EACCES: %s\n",strerror(EACCES));6     errno=ENOENT;7     perror(argv[0]);8     exit(0);9 }

 

1.8用户标识

用户ID 和组ID

示例:1_7 打印用户ID和组ID

1 #include"apue.h"2 int main()3 {4     printf("uid=%d,gid= %d,\n",getuid(),getgid());5     exit(0);6 }

1.9信号

信号是通知进程已发生某种情况的一种技术。

进程处理信号有三种选择:a.忽略该信号

            b.按系统默认方式处理。对于除以0的情况,系统默认是终止该进程

            c.提供一个函数,信号发生时则调用该函数,这被称为捕捉该信号。使用这种方式,我们只要提供自编的函数就将能知道什么时候产生了信号,并按所希望             的方式处理它

实例:1_8 从标准输入读命令并执行(比1_5多了信号处理)

 1 #include"apue.h" 2 #include<sys/wait.h> 3 static void sig_int(int); 4  5 int main(void) 6 { 7     char buf[MAXLINE]; 8     pid_t pid;     9     int status;10     if(signal(SIGINT,sig_int)==SIG_ERR)11     err_sys("signal error");12 13     printf("%% ");14     while(fgets(buf,MAXLINE,stdin)!=NULL){15     if(buf[strlen(buf)-1]==\n)16     buf[strlen(buf)-1]=0;17     if((pid=fork())<0){18     err_sys("fork error");19     }20     else if(pid==0){21     execlp(buf,buf,(char *)0);22     err_ret("couldn‘t execute:%s",buf);23     exit(127);24     }25     if((pid=waitpid(pid,&status,0))<0)26     err_sys("waitpid error");27     printf("%% ");28     }29     exit(0);30 }31     void sig_int(int signo)32     {33         printf("interrupt\n%%");34     }

1.10时间值

UNIX系统使用三个进程时间值:时钟时间,用户cpu时间,系统cpu时间

时钟时间又称为墙上时钟时间(wall clock time)。它是进程运行的时间总量,其值与系统中跟同时运行的进程数有关。

用户CPU时间是执行用户指令所用的时间。

系统cpu时间是为该进程执行内核程序所经历的时间

例如:每当一个进程执行一个系统服务时,如read或write,则在内核内执行该服务所花费的时间就计入该进程的系统cpu时间。用户cpu时间和系统cpu时间之和常被称为cpu时间

 

 

取得任意进程的时钟时间,用户时间,和系统时间:$cd /usr/include;time -p grep _POSIX_SOURCE */*.h > /dev/null