天天看点

《apue》读书笔记 第一章 UNIX基础知识第一章 UNIX基础知识

第一章 UNIX基础知识

1.UNIX体系结构

系统调用:内核的接口;

公用函数库:构建在系统调用接口之上;

shell:为运行其他应用程序提供了一个接口。

《apue》读书笔记 第一章 UNIX基础知识第一章 UNIX基础知识

UNIX操作系统的体系结构

2.登陆

2.1用户名

系统是用数值id来表示用户的,但为方便可读性,id和用户名之间存在一一映射。

系统口令文件在/etc/passwd中,为保证密码安全,加密口令转移到另一个文件中了。

2.2shell

我们linux常用的Bash,全名是Bourne-again shell,是GNU shell,被设计成遵循POSIX的,支持C shell和Korn shell两者的特色功能。

(在linux中,/bin/sh将链接到/bin/bash)

3.文件和目录

3.1文件系统

3.2文件名

文件名中不能出现斜线(/)和空操作符(null)。

斜线用来分隔构成路径名的各文件名,空操作符用来终止一个路径名。

. 表示当前目录;

.. 表示上一层目录。

3.3路径名

绝对路径:以斜线开头;

相对路径:相对于当前目录的路径。

ls(1)命令的简单实现:

/*
    列出一个目录中的所有文件
 */
#include "apue.h"
#include <dirent.h>

int 
main(int argc, char *argv[])
{
    DIR             *dp;
    struct dirent   *dirp;

    if (argc != )
        err_quit("usage: ls directory_name");

    if ((dp = opendir(argv[])) == NULL)
        err_sys("can't open %s", argv[]);

    while ((dirp = readdir(dp)) != NULL)
        printf("%s\n", dirp->d_name);

    closedir(dp);
    exit();
}
           

家目录:登陆时的默认目录,一般为/home/用户名。

4.输入和输出

4.1文件描述符号

文字描述符是一个小的非负整数,内核用以标识一个特定进程正在访问(读或者写)的文件。当内核打开一个或创建一个文件时,它就返回一个文件描述符。然后当读、写文件时,就可使用它。

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

一般,每当运行一个新程序时,所有的shell都为其打开三个文件描述符:标准输入、标准输出以及标准错误。

4.3不带缓冲的I/O

函数open,read,write,lseek,close提供了不带缓冲的I/O。

所谓不带缓冲,就是告诉系统调用操作多少,那么它就会尽量操作这些数据然后返回。

而对于带缓冲的,一般会等到缓冲区满了才进行操作。

4.4标准I/O

提供了带缓冲的接口。

5.程序和进程

程序:存放在磁盘上,可执行;

进程:实例化的程序,每个进程有唯一个数字标识符,为进程ID;

3个主要的进程控制函数:fork,exec和waitpid:

fork创建一个新的子进程,是父进程的复制品,fork向父进程返回子进程ID,向子进程返回0;

在子进程中,调用execlp执行新程序文件,这就用新的程序文件来替换了子进程原先执行的程序文件,因此产生了一个新的进程;

如果父进程希望等待子进程终止,则用waitpid。

我们可以使用getpid()来获得进程id,它返回一个pid_t数据类型,我们不知道它的大小,但是标准保证它能存放在一个long int中。

从标准输入读入命令并执行(不能传参数):

#include "apue.h"
#include <sys/wait.h>

int
main(void)
{
    char    buf[MAXLINE];   /* from apue.h */
    pid_t   pid;
    int     status;

    printf("%% ");  /* print prompt (printf requires %% to print %) */
    while (fgets(buf, MAXLINE, stdin) != NULL) {
        if (buf[strlen(buf) - ] == '\n')
            buf[strlen(buf) - ] = ; /* replace newline with null */

        if ((pid = fork()) < ) {
            err_sys("fork error");
        } else if (pid == ) {      /* child */
            execlp(buf, buf, (char *));
            err_ret("couldn't execute: %s", buf);
            exit();
        }

        /* parent */
        if ((pid = waitpid(pid, &status, )) < )
            err_sys("waitpid error");
        printf("%% ");
    }
    exit();
}
           

一些个人的理解:我们使用bash 下达命令时,产生的新进程是bash的子进程。

线程:同一进程的所有线程共享同一地址空间,文件描述符,栈以及进程相关属性。

6.出错处理

当UNIX函数出错时,往常返回一个负值,同时将整型变量errno设置为具有特定信息的一个值。例如,open函数如成功执行则返回一个非负文件描述符,如出错则返回-1,同时设置errno。在open出错时,有大约15种不同的errno值(文件不存在,许可权问题等)。

C标准定义了两个函数来帮助打印错误信息:

char *strerror(int errnum); //返回:指向消息字符串的指针

void perror(const char *msg); //输出由msg指向的字符串,然后是一个冒号,一个空格,接着是对应于errno值的出错信息,最后换行。

例子:

#include "apue.h"
#include <errno.h>

int
main(int argc, char *argv[])
{
    fprintf(stderr, "EACCES: %s\n", strerror(EACCES));
    errno = ENOENT;
    perror(argv[]);
    exit();
}
           

编译送到a.out,执行和结果:

$ ./a.out

EACCES: Permission denied
./a.out: No such file or directory
           

出错分类:致命错误和非致命错误;

7.用户标识

用户id(/etc/passwd),组id(/etc/group),附加组id(一个用户可以属于多个组)。

(用id来存储,假设每个值用双字节整型值存放,则这两个值只需要4个字节)

8.信号

信号处理:

  • 忽略该信号;
  • 按系统默认方式处理;
  • 提供一个函数,信号发生时调用该函数——捕捉该信号。

当向一个进程发信号时,我们需要是该进程的拥有者或者超级用户。

9.时间值

时钟时间, 用户CPU时间, 系统CPU时间。

10.系统调用和函数

应用程序代码——系统调用 or

应用程序代码——C库函数 or

应用程序代码——C库函数——系统调用;

例子: 应用程序——malloc分配内存——sbrk系统调用。

系统调用通常提供一个最小接口,函数通常实现比较复杂的功能。

(UNIX中,每个系统调用在标准C库中设置一个具有相同名字的函数,所以我们可以通过标准C库来进行系统调用)

继续阅读