• apue学习笔记(第一章UNIX基础知识)


    总所周知,UNIX环境高级编程是一本很经典的书,之前我粗略的看了一遍,感觉理解得不够深入。

    听说写博客可以提高自己的水平,因此趁着这个机会我想把它重新看一遍,并把每一章的笔记写在博客里面。

    我学习的时候使用的平台是Windows+VMware+debian,使用secureCRT来连接(可以实现多个终端连接)。

    因为第一章是本书大概的描述,所以第一章的我打算写得详细一点,而且书本的原话占的比例会比较多,重点的东西会用粗体显示出来。

    1.1  引言

    所有操作系统都为他们所运行的程序提供服务。典型的服务包括:执行新程序、打开文件、读文件、分配存储区以及获取当前时间等,

    本书集中阐述不同版本的UNIX操作系统所提供的服务。

    本章会简要的介绍UNIX提供的各种服务,在以后的各章中将对这些概念做更详细的说明。

    1.2  UNIX体系结构

    从严格意义上说,可将操作系统定义为一种软件,它控制计算机硬件资源,提供程序运行环境。我们通常将这种软件称为内核

    下图显示了UNIX系统的体系结构,其它部分下面会逐个讲到。

    1.3  登录

    1 登录名

    用户在登录UNIX系统时,先键入登录名,然后键入口令。系统在其口令文件(通常是/etc/passwd文件)中查看用户名。

    2 shell

    用户登录后,系统通常先显示一些系统信息,然后用户就可以向shell程序键入命令。

    shell是一个命令行解释器,它读取用户输入,然后执行命令。

    1.4  文件和目录

    1 文件系统

    UNIX文件系统是目录和文件的一种层次结构,所有东西的起点都是根的目录,这个目录名称是一个字符“/”

    目录是一个包含目录项的文件,可以认为每个目录项都包含一个文件名,同时还包含该文件属性的信息(ls -l可以查看)。第四章会详细说明文件的各种属性。

    2 文件名

    目录中的各个名字成为文件名,创建目录时会自动创建两个文件名:.(当前目录)和..(父目录)   (可见为什么 cd .. 会回到父目录)

    3 路径名

    已斜线开头的路径名称成为绝对路径名(可以理解为以根目录开头的相对路径名),否则为相对路径名。

    不难列出一个目录中所有文件的名字,下面是ls(1)命令的简要实现

    其中apue.h可以通过网上下载,http://blog.csdn.net/istruth/article/details/44201827有下载地址和使用的方法,后面章节会详细的给出下列函数的用法。

    #include "apue.h"
    #include <dirent.h>
    
    int main(int argc,char *argv[])
    {
        DIR *dp;
        struct dirent *dirp;
        if(argc!=2)
            err_quit("usage:ls directory_name");
        if((dp=opendir(argv[1]))==NULL)
            err_sys("can't open %s",argv[1]);
        while((dirp=readdir(dp))!=NULL)
            printf("%s
    ",dirp->d_name);
        closedir(dp);
        exit(0);
    }

    可以使用gcc编译该文件  执行命令cc myls.c进行编译   会生成a.out文件  执行./a.out命令来运行  (./代表当前目录,如果不指定,系统会在环境变量中设置的路径中找可执行文件)

    4  工作目录

    每个进程都有一个工作目录,有时称其为当前工作目录,进程可以用chdir函数更改其工作目录

    5 起始目录

    登录时,工作目录设置为起始目录,该起始目录从口令文件中相应用户的登录项中取得。

    1.5  输入与输出

    1 文件描述符

    文件描述符通常是一个小的非负整数,用来标识一个特定进程正在访问的文件。当内核打开一个现有文件或创建一个新文件时,

    它都返回一个文件描述符,可以通过这个文件描述符进行读写文件

    2  标准输入、标准输出和标准错误

    每当运行一个新程序时,所有的shell都为其打开3个文件描述符,即标准输入(0)、标准输出(1)和标准错误(2),如果不做特殊处理,

    则这3个描述符都链接到终端。

    3  不带缓冲的I/O

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

    下面程序展示了如何复制UNIX下的普通文件

    #include "apue.h"
    
    #define BUFFSIZE 4096
    
    int main(void)
    {
        int n;
        char buf[BUFFSIZE];
        while((n=read(STDIN_FILENO,buf,BUFFSIZE))>0)
            if(write(STDOUT_FILENO,buf,n)!=n)
                err_sys("write error");
        if(n<0)
            err_sys("read error");
        exit(0);
    }

    大多数shell都提供一种方法,使其中任何一个或所有这3个描述符都能重定向到某个文件。例如ls > file.list 可以将ls的输出(标准输出 文件描述符为1)重定向到名为file.list的文件。

    如下执行上面的程序: ./a.out > data 可以把程序的标准输出重定向到文件data   ./a.out < infile > outfile 把程序的标准输入重定向到infile  把标准输出重定向到outfile,实现了文件的复制。

    4  标准I/O

    标准I/O 为那些不带缓冲的I/O函数提供了一个带缓冲的接口,下面演示使用标准I/O复制UNIX文件

    #include "apue.h"
    
    int main(void)
    {
        int c;
        while((c=getc(stdin))!=EOF)
            if(putc(c,stdout)==EOF)
                err_sys("output error");
        if(ferror(stdin))
            err_sys("input error");
        exit(0);
    }

    1.6 程序与进程

    1 程序

    程序是一个存储在磁盘上某个目录中的可执行文件

    2 进程和进程ID

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

    下面程序用于打印进程ID

    #include "apue.h"
    
    int main(void)
    {
        printf("hello world from process ID %ld
    ",(long)getpid());
        exit(0);
    }

    3 进程控制

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

    UNIX系统的进程控制功能可以用一个简单得程序说明,下面程序从标准输入读取命令,然后执行命令,类似于shell程序的基本实施部分。

    #include "apue.h"
    #include <sys/wait.h>
    
    
    int main(void)
    {
        char buf[MAXLINE];
        pid_t pid;
        int status;
        printf("%% ");
        while(fgets(buf,MAXLINE,stdin)!=NULL)
        {
            if(buf[strlen(buf)-1]=='
    ')
                buf[strlen(buf)-1]=0;
            if((pid=fork())<0)
                err_sys("fork error");
            else if(pid==0)
            {
                execlp(buf,buf,(char *)0);
                err_ret("couldn't execute:%s",buf);
                exit(127);
            }
            if((pid=waitpid(pid,&status,0))<0)
                err_sys("waitpid error");
            printf("%% ");
        }
        exit(0);
    }

    4  线程和线程ID

    与进程相同,线程也用ID标识。但是,线程ID只在它所属的进程内起作用。

    1.7  出错处理

    当UNIX系统函数出错时,通常会返回一个负值,而且整形变量errno通常被设置为具有特定信息的值。

    文件<error.h>中定义了error以及可以赋予它的各种常量。

    C标准定义了两个函数,用于打印出错信息

    #include <string.h>
    char *strerror(int errnum);

    strerror函数将errnm(通常就是error值)映射为一个出错消息字符串,并且返回此字符串的指针。

    #include <stdio.h>
    void perror(const char *msg);

    perror函数基于errno的当前值,在标准错误上产生一条出错信息,然后返回。

    下面程序显示这两个出错函数的使用方法

    #include "apue.h"
    #include <errno.h>
    
    int main(int argc,char *argv[])
    {
        fprintf(stderr,"EACCES:%s
    ",strerror(EACCES));
        errno=ENOENT;
        perror(argv[0]);
        exit(0);
    }

    1.8  用户标识

    1 用户ID

    用户ID是一个数值,用来确定一个用户。用户ID为0为根用户或超级用户,超级用户对系统有自由的支配权。

    2 组ID

    组ID是由系统管理员在指定用户登录名时分配的,可以把多个用户分成一组。

    下面程序用于打印用户ID和组ID

    #include "apue.h"
    
    int main(void)
    {
        printf("uid=%d,gid=%d
    ",getuid(),getgid());
        exit(0);
    }

    3 附属组ID

    除了在口令文件中对一个登录名指定一个组ID之外,大多数UNIX系统版本还允许一个用户属于另外一些组。

    1.9 信号

    信号用于通知进程发生了某种情况。例如,若某一进程执行除法操作,其除数为0,则将名为SIGFPE(浮点异常)的信号发送给进程。

    进程有以下3种处理信号的方式:

    1 忽略信号

    2 按系统默认方式处理

    3 提供一个函数,信号发生时调用该函数,这被称为捕捉该信号。

    终端键盘上有两种产生信号的方法,分别称为中断键(通常是Delete或Ctrl+C)和退出键(通常是Ctrl+)

    修改之前的shell实例,使程序可以捕获SIGINT信号,我们会在第10章详细的介绍信号。

    #include "apue.h"
    #include <sys/wait.h>
    
    static void sig_int(int);
    
    int main(void)
    {
        char buf[MAXLINE];
        pid_t pid;
        int status;
        if(signal(SIGINT,sig_int)==SIG_ERR);
                    err_sys("signal error");
        printf("%% ");
        while(fgets(buf,MAXLINE,stdin)!=NULL)
        {
            if(buf[strlen(buf)-1]=='
    ')
                buf[strlen(buf)-1]=0;
            if((pid=fork())<0)
                err_sys("fork error");
            else if(pid==0)
            {
                execlp(buf,buf,(char *)0);
                err_ret("couldn't execute:%s",buf);
                exit(127);
            }
            if((pid=waitpid(pid,&status,0))<0)
                err_sys("waitpid error");
            printf("%% ");
        }
        exit(0);
    }
    void sig_int(int signo)
    {
            printf("interrupt
    %%");
    }
        

    1.10  时间值

    历史上,UNIX系统使用过两种不同的时间值

    1 日历时间。该值是从1970年1月1日00:00:00这个特定时间以来所经过的秒数的累计值。

    2 进程时间。也被称为CPU时间

       当度量一个进程的执行时间时,UNIX系统为一个进程维护了3个进程时间值

       时钟时间  即进程运行的时间总量,其值与系统中同时运行的进程数有关

       用户CPU时间  执行用户指令所用的时间量 

     系统CPU时间  执行系统调用的时间

    1.11 系统调用和库函数

    系统调用提供的函数如open, close, read, write, ioctl等,系统调用发生在内核空间

    标准C库函数提供的文件操作函数如fopen, fread, fwrite, fclose, fflush, fseek等属于库函数,底层也是通过系统调用来实现的

  • 相关阅读:
    android 之MediaPlayer MP3播放,VideoView 视频播放,MediaRecorder 录音
    IPHONE 开发 4 iPhone应用程序目录构成,工程项目的构成
    iPhone交流
    UILabel 用法
    跨网段 访问
    IPHONE 开发 8 Object C Foundation.h它包括所有的类 方法 集合,(id)init,iphone上使用Sqlite的注意事项小结
    查看与某一个表相关的视图、存储过程、函数
    IPHONE 开发 1 体系介绍
    android 之手机拨号器,以及短信发送器的简单实现
    Linux命令整理
  • 原文地址:https://www.cnblogs.com/runnyu/p/4630511.html
Copyright © 2020-2023  润新知