unix高级环境编程-读书笔记(1)

来源:互联网 发布:网络维修工具包 编辑:程序博客网 时间:2024/05/16 17:13

在研读了鸟哥的linux私房菜之后,对linux有了大致的概念,下面就开始研读linux学习中被称为圣经的:《Advanced Programming in unix environment》,也就是众所周知的unix环境高级编程。


unix体系结构:

操作系统实际上是一个软件,也就是内核,而内核的接口被称为系统调用(system call),公用函数库构建在系统调用接口之上,应用程序既可调用函数库,也可使用系统调用。

登陆shell:

shell 是一个命令解释器,读取用户输入,然后执行命令,用户有时通过终端shell,有时通过文件(shell脚本)向shell进行输入,常见的shell如下所示

文件和目录:

unix文件系统是目录和文件组成的一种层次结构,当创建新目录时会自动创建两个文件名,   .和.. ,其中点指向当前目录,点点指向父目录,在最高层次的根目录中,.和..没有区别。


ls的c语言编程实现

准备工作:linux下c的头文件都放在/usr/include目录下,在unix这本书中,有很多源代码,但里面都包含了一个头文件apue.h,是作者自己写的,linux并不包含这个,因此需要在官网下载这个头文件,并放在/usr/include下,官网:http://www.apuebook.com/。并且要在apue.h的最后一行,也就是#endif之前一行,加上#include "error.c"。然后将下载的源代码中..apue.2e/lib目录下找到error.c这个文件,放到/usr/include目录下即可。

源代码:

#include"apue.h"#include <dirent.h>     int main(   int   argc   ,   char   *argv[])  //将命令行的第一个参数argv[1]作为目录名。   {     DIR *dp;   struct dirent *dirp;   if(argc !=2)                                                //当命令行参数不等于2,也就是没有写要列出的目录时,会报错     err_quit("usage: ls directory_name");    if((dp =opendir(argv[1]))==NULL)      //调用opendir函数,返回指向dir结构的指针,当返回值dp是null,表明不存在这个目录     err_sys("can't open %s",argv[1]);        while ((dirp = readdir(dp)) !=NULL)   //将这个dp指针传递给readdir,这里用到while循环结构,一直到readdir函数无目录可读返回null时,结束程序。     printf("%s\n",dirp->d_name);   closedir(dp);   exit(0);                                                   //程序结束,以参数0调用exit函数,表示正常结束,参数值1-255表示出错。   }  

编译好后,默认名是a.out

也就是./a.out 相当于ls命令

比如


程序和进程:

unix确保每一个进程都有一个唯一的数字标识符,称为进程ID,进程ID总是一非负数。

源代码:打印进程ID

#include "apue.h"intmain(void){printf("heollo world from process ID %d\n",getgid());  //调用getgid函数来调用进程IDexit(0);}

getpid返回一个pid_t数据类型,我们不知道其大小,仅知道的是标准会保证它能保存在一个长整型中。

进程控制:

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

源代码:从标准输入命令并执行


#include "apue.h"#include <sys/wait.h>int main(){ char  buf[MAXLINE]; pid_t pid; int   status; printf("%% "); while (fgets(buf,MAXLINE,stdin)!=NULL) //标准fgets从标准输入一次读一行,当输入文件结束字符(ctrl+d)作为行的第一个字符                                                                          //,fgets返回一个null,循环终止,退出进程 {,  if(buf[strlen(buf) -1]=='\n')                     //用null代替换行符,因为execlp函数要求参数以null结束     buf[strlen(buf) -1]=0;     if((pid=fork())<0)                                    //调用fork创建一个新进程,这个新进程称为子进程,fork向父进程返回子进程的ID(非负),对子进程返回0   {     err_sys("fork error");    }   else if(pid == 0)                               //子进程   {    execlp(buf,buf,(char *)0);                //在子进程中,调用execlp以执行从标准输入读入的命令,    err_ret("couldn't execute: %s",buf);    exit(127);   }               if((pid = waitpid(pid, &status,0))<0)    // 父进程等待子进程结束,waitpid返回子进程的终止状态,     err_sys("waitpid error");  printf("%% "); }  exit(0);}

这个程序利用标准IO函数fgets从标准输入一次读一行,execlp函数执行新程序文件。

int execlp(const char * file,const char * arg,....);

execlp()会从PATH 环境变量所指的目录中查找符合参数file的文件名,找到后便执行该文件,然后将第二个以后的参数当做该文件的argv[0]、argv[1]……,最后一个参数必须用空指针NULL)作结束。如果用常数0来表示一个空指针,则必须将它强制转换为一个字符指针,否则将它解释为整形参数,如果一个整形数的长度与char * 的长度不同,那么exec函数的实际参数就将出错。如果函数调用成功,进程自己的执行代码就会变成加载程序的代码,execlp()后边的代码也就不会执行了。

该程序的主要限制就是不能向所执行的命令传递参数,例如不能指定要列出目录项的目录名,只能对工作目录执行ls命令。如果要传递参数,先要分析输入行,然后用某种约定将参数分开,再传递给execlp函数。

信号:

通知进程已发生某种情况的一种技术,进程如何处理信号有三种选择,1.忽略该信号;2 按系统默认的方式处理 ;3 提供一个函数,信号发生时调用这个函数。

终端键盘上有两种产生信号的方法,分别称为中断键(delete或者crtl+c)和退出健。被用于中断当前的进程,另一种产生信号的方法是调用名为kill的函数。


出错处理:

当unix系统函数出错时,通常返回一个负值,而且整型变量errno通常被设置为具有特定信息的值,在open函数出错时,有大约15种不同的errno值。

头文件errno.h定义了errno以及可以赋予它的各种常量,这些常量都是以字符E开头。


时间值:

当度量一个进程的执行时间时,unix系统使用三个进程时间值:

时钟时间(墙上时间):也就是进程从开始到结束所用的实际时间

用户cpu时间:执行用户指令所用的时间

系统cpu时间:该进程执行内核程序所用时间

后两者时间和常称为cpu时间,执行time()指令,可以得到这三个值。

那么如果多核cpu并行处理指令,会导致时钟时间(wall time)会小于后俩者之和。


系统调用和库函数:

从实现者角度系统调用和库函数之间有重大区别,但是对于用户,区别并不重要。但是应该知道的是,必要时我们可以替代库函数,却无法替代系统调用

以存储器分配函数malloc,它实际上是调用了系统调用函数sbrk。也就是说,很多库函数是调用系统调用,而应用程序既可以调用库函数,也可以调用系统调用。


系统调用和库函数另一个差别是:系统调用通常提供一种最小接口,而库函数通常提供比较复杂的功能。


0 0