UNIX学习基础

抱歉了, 现在只能做成这样, 因为这是我在vim里面写的笔记再整理的笔记了, 有点随笔, 但是干货还是有点, 看的挺慢的, 借鉴了很多人的知识, 有些不好写也就没有写, 也怕写错, 毕竟自己也才开始. 本来用ATOM写的, 但是, 这玩意居然安不了插件, 无奈这几天才用vim写的. 刚开始看的人估计都跟我一样, 很多不懂, 所以我也查了各种资料, 所以写的很浅显. 以后写的好了再来补充, 我继续整改我的配置了

主要知识

涉及的函数:
在 ls.cpp 代码部分, 文件类型;
start();

DIR 数据类型. opendir(), readdir(), closedir();

在 va_list.cpp 代码部分:
va_list(), va_start(), va_arg(), va_end();

在 getID.cpp 代码部分, 获得ID;
getpid(), getuid(), getgid();

在 process_control.cpp 代码里面, 进程;
fork(), waitpid();

/* ************************************************************************
    > File Name: ls.cpp
    > Author: Function_Dou
    > Created Time: 20180118日 星期四 110055秒
    > 第一次修改 : 增加了err_sys, err_quit的注释, 来源.  18.1.18/ 13:11

    > agrc : 代表传入的参数的个数
    > agrv[] : 代表传入的变量, agrv[0]代表文件名
    > 函数 DIR *opendir(const char *pathname),即打开文件目录返,回的就是指向DIR结构体的指针,而该指针由以下几个函数使用:
    struct dirent *readdir(DIR *dp);

    DIR *opendir(const char *pathname);     // 第一步, 打开文件, 执行初始化操作

    void rewinddir(DIR *dp);                // 返回目录中的第一个目录项

    int closedir(DIR *dp);                  // 最后一步, 关闭文件

    long telldir(DIR *dp);

    void seekdir(DIR *dp,long loc);

    dirent不仅仅指向目录,还指向目录中的具体文件,readdir函数同样也读取目录下的文件

    > dirent结构体存储的关于文件的信息很少,所以dirent同样也是起着一个索引的作用,如果想获得类似ls -l那种效果的文件信息,必须要靠stat函数了
    eaddir函数读取到的文件名存储在结构体dirent的d_name成员中,而函数

    int stat(const char *file_name, struct stat *buf);

    struct stat
    {

            mode_t     st_mode;       //文件访问权限

            ino_t      st_ino;       //索引节点号

            dev_t      st_dev;        //文件使用的设备号

            dev_t      st_rdev;       //设备文件的设备号

            nlink_t    st_nlink;      //文件的硬连接数

            uid_t      st_uid;        //所有者用户识别号

            gid_t      st_gid;        //组识别号

            off_t      st_size;       //以字节为单位的文件容量

            time_t     st_atime;      //最后一次访问该文件的时间

            time_t     st_mtime;      //最后一次修改该文件的时间

            time_t     st_ctime;      //最后一次改变该文件状态的时间

            blksize_t st_blksize;    //包含该文件的磁盘块的大小

            blkcnt_t   st_blocks;     //该文件所占的磁盘块

     };
**************************************************************/

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

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

    // err_sys, err_quit : 是apue.h里面的自定义的, 原型是用va_list(), va_start(), va_end()写的, 了解可看文件va_list.cpp
    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\n", dirp->d_name);

    closedir(dp);
    exit(0);
}
/***********************************************************************
    > File Name: va_list.cpp
    > Author: Function_Dou
    > Created Time: 20180118日 星期四 124431秒

    VA_LIST 是在C语言中解决变参问题的一组宏,变参问题是指参数的个数不定,可以是传入一个参数也可以是多个;可变参数中的每个参数的类型可以不同,也可以相同;可变参数的每个参数并没有实际的名称与之相对应,用起来是很灵活

    VA_LIST的用法:
       (1)首先在函数里定义一具VA_LIST型的变量,这个变量是指向参数的指针;
       (2)然后用VA_START宏初始化变量刚定义的VA_LIST变量;
       (3)然后用VA_ARG返回可变的参数,VA_ARG的第二个参数是你要返回的参数的类型(如果函数有多个可变参数的,依次调用VA_ARG获取各个参数);
       (4)最后用VA_END宏结束可变参数的获取。



****************************************************************

    #define _INTSIZEOF(n)   ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
    目的是将求出n对齐后的字节数,从这里来看应该是按照4字节的整数倍来对齐

    va_list ap ;  定义一个va_list变量ap

    va_start(ap,v) ;执行ap = (va_list)&v + _INTSIZEOF(v),ap指向参数v之后的那个参数的地址,即 ap指向第一个可变参数在堆栈的地址。

    va_arg(ap,t) , ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )取出当前ap指针所指的值,并使ap指向下一个参数。
    实现将ap指向的参数取出赋给t,并且让p指向下一个可变参数 具体是先(ap += _INTSIZEOF(t)) 之后用 (*( t *)(ap-_INTSIZEOF(t))),设计的也很巧.

    va_end(ap) ; 清空va_list ap。
    #define va_end(ap) ( ap = (va_list)0 )
    最后将ap指针设置为空。

typedef char *  va_list;  va_list 就是char *类型的指针。
 ************************************************************************/


// 打印字符串

#include <stdarg.h>
#include <stdio.h>
#include "apue.h"

void Function(char *str, ...)
{
    char *str1;

    str1 = str;
    va_list v1;
    va_start(v1, str);
    while(str1)
    {
        printf("%s\t", str1);
        str1 = va_arg(v1, char *);

    }

    va_end(v1);
}


int main()
{
    Function ("one", "two", "three", NULL);



    exit(0);
}

/*
#include <stdio.h>
#include <stdarg.h>
#include "apue.h"

void ar_cnt(int cnt,...);
void ar_cst(char const *s,...);

int main()
{
    //int in_size = _INTSIZEOF(int);
    int in_size = 4;

    printf("int_size=%d\n",in_size);
    ar_cnt(5,1,2,3,4);

    return 0;
}

void ar_cnt(int cnt,...)
{
    int value1=0;
    int i=0;

    va_list arg_ptr;
    va_start(arg_ptr,cnt);

    for(i=0;i<cnt;i++)
    {
        value1=va_arg(arg_ptr,int);
        printf("posation %d=%d\n",value1,i+1);
    }

    va_end(arg_ptr);
}
/*************************************************************************
    > File Name: getID.cpp
    > Author: Function_Dou
    > Created Time: 2018年01月18日 星期四 13时12分59秒
 ************************************************************************/
#include <stdio.h>
//#include <iostream>
#include <unistd.h>
#include "apue.h"
//using namespace std;


int main()
{
    // getpid() : 在头文件unistd.h文件里面
    // 取得进程识别码,许多程序利用取到的此值来建立临时文件,以避免临时文件相同带来的问题
    printf("process ID %ld uid = %d. gid = %d\n", (long)getpid(), getuid(), getgid());

    return 0;
}


/*
样列
process ID 3864 uid = 0. gid = 0
process ID 3999 uid = 0. gid = 0
*/
/*************************************************************************
    > File Name: process_control.cpp
    > Author: Function_Dou
    > Created Time: 2018年01月18日 星期四 13时35分10秒
 ************************************************************************/

/************************************************************************

    fork() : 通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事,但如果初始参数或者传入的变量不同,两个进程也可以做不同的事。
    一个进程调用fork()函数后,系统先给新的进程分配资源,例如存储数据和代码的空间。然后把原来的进程的所有值都复制到新的新进程中,只有少数值与原来的进程的值不同。相当于克隆了一个自己。


    > fork调用的一个奇妙之处就是它仅仅被调用一次,却能够返回两次,它可能有三种不同的返回值:
     1)在父进程中,fork返回新创建子进程的进程ID;
     2)在子进程中,fork返回0;
     3)如果出现错误,fork返回一个负值;

    进程执行没有固定的先后顺序,哪个进程先执行要看系统的进程调度策略。



    execlp() : 从PATH 环境变量中查找文件并执行
    execlp() 会从PATH 环境变量所指的目录中查找符合参数file的文件名, 找到后便执行该文件, 然后将第二个以后的参数当做该文件的argv[0]、argv[1]……, 最后一个参数必须用空指针(NULL)作结束。
    如果执行成功则函数不会返回,执行失败则直接返回-1,失败原因存于errno 中



    execlp() : 要求参数必须以NULL结尾, 而不是以'\n'结尾. 一般采用 execlp($1, $2, (char *)0)进行将末尾设置为 NULL;
    fork() : 创建一个新进程, 一般在函数中创建是为子进程;
    调用execlp() 以执行从标准输入读入指令, fork()跟execlp()就是操作系产生新进程;
    waitpid() : 指令目前进程要等待的进程. waitpid()返回等待的子进程的终止状态(ststus);
    waitpid()会暂时停止目前进程的执行, 直到有信号来到或子进程结束. 如果在调用wait()时子进程已经结束, 则wait()会立即返回子进程结束状态值. 子进程的结束状态值会由参数status 返回, 而子进程的进程识别码也会一快返回. 如果不在意结束状态值, 则参数status 可以设成NULL. 参数pid 为欲等待的子进程识别码

    与进程相同, 线程也用ID标识. 但是线程ID只在所属的进程内其作用, 在其他进程就没有意义了, 即, 线程属于进程;

*************************************************************************/
#include <stdio.h>
#include <sys/wait.h>
#include <string.h>
#include "apue.h"

int main()
{
    char buf[MAXLINE];
    pid_t pid;
    int status;

    printf("%%");

    while(fgets(buf, MAXLINE, stdin) != NULL)
    {
        if(buf[strlen(buf) - 1] ==  '\n')
            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("%%");
    }


    return 0;
}

/*****************fork() 的一个示例****************************
#include <unistd.h>
#include <stdio.h>
int main ()
{
    pid_t fpid; //fpid表示fork函数返回的值
    int count=0;
    fpid=fork();
    if (fpid < 0)
        printf("error in fork!");
    else if (fpid == 0)
    {
        printf("i am the child process, my process id is %d\n",getpid());
        printf("我是爹的儿子\n");
        count++;
    }
    else
    {
        printf("i am the parent process, my process id is %d\n",getpid());
        printf("我是孩子他爹\n");
        count++;
    }
    printf("统计结果是: %d\n",count);
    return 0;
}
*/

stat和fstat函数返回包含所有文件属性的一个信息结构;

UNIX文件系统大多数实现并不在目录项中存放属性,因为当一个文件链接多个硬链接时,很难保证多个副本之间的属性同步;
示例在process_control.cpp

execlp() : 要求参数必须以NULL结尾, 而不是以’\n’结尾. 一般采用 execlp( 1, 2, (char *)0)进行将末尾设置为 NULL;
fork() : 创建一个新进程, 一般在函数中创建是为子进程;
调用execlp() 以执行从标准输入读入指令, fork()跟execlp()就是操作系产生新进程;
waitpid() : 指令目前进程要等待的进程. waitpid()返回等待的子进程的终止状态(ststus);
与进程相同, 线程也用ID标识. 但是线程ID只在所属的进程内其作用, 在其他进程就没有意义了, 即, 线程属于进程;
perror() : 将上一个函数发生错误的原因输出到标准设备(stdin), 错误原因依照error的值来决定将要输出的字符串;

时间值:

  1. 时钟时间 : 又称墙上时间, 它是进程运行的总时间量, 其值与同时运行的总进程数有关
  2. 用户CPU时间 : 执行用户命令所用时间量
  3. 系统CPU时间 : 进程执行内核所用的时间

参考网址 : http://blog.csdn.net/sgbfblog/article/details/7772153

sbrk() : 按指定的字节分配内存.sbrk(1) 每次让堆往栈的方向增加 1 个字节的大小空间;
brk() 和 sbrk() 改变 “program brek” 的位置,这个位置定义了进程数据段的终止处(也就是说,program break 是在未初始化数据段终止处后的第一个位置);

缓存

应用缓存是一次性读入大量的数据进行缓冲, 这样之后不是读操作都要调用read, 而是可以直接在缓冲区读数据
管理员模式和用户模式之间的切换需要消耗时间,相比之下,磁盘的I/O操作消耗的时间更多,为了提高效率,内核也使用缓冲技术来提高对磁盘的访问速度。

程序读取数据的时候只能通过系统的read进行读
read把数据从内核缓冲区复制到进程缓冲区中, write把数据从进程缓冲区复制到内核缓冲区中.但他们并不代表数据在内核和磁盘之间的交换
应用内核缓冲区技术, 提高磁盘的I/O效率, 优化磁盘的读写操作. 当写入磁盘时调用write之后, 会调用ffulsh将数据写入磁盘, 以防止数据的意外丢失.
内核可以在任何时候写磁盘,但并不是所有的write操作都会导致内核的写动作。内核会把要写的数据析时存在缓冲区中.积累到一定数量后再一次写人。有时会导致意外情况,比如突然断电,内核还来不及把内核缓冲区中的数据写到磁盘上,这些更新的数据就会丢失。
从理论上讲,内核可以在任何时候写磁盘,但并不是所有的write操作都会导致内核的写动作。内核会把要写的数据析时存在缓冲区中.积累到一定数量后再一次写人。有时会导致意外情况,比如突然断电,内核还来不及把内核缓冲区中的数据写到磁盘上,这些更新的数据就会丢失。

不带缓冲指每个read, write都调用内核的一个系统调用
不同缓冲长度对read和write的影响

文件描述符

对于内核, 每次打开文件都将通过文件描述符引用. 文件描述符是一个非负整数. 当打开或创建一个文件时, 内核都会向进程返回一个文件描述符. 返回的文件描述符相当于告诉进程, 它能对文件做什么操作.
文件的符号STDIN_FILENO(0), STDOUT_FILENO(1), STDERR_FILENO(2).

open(const char* path, …)与openat(int fd,const cahr* path, …)
1. path参数指定的是绝对路径时, fd可以忽略, open就相当与openat
2. path参数指定的是相对路径时, fd参数指出了相对路径名在系统中的开始地址, fd参数是通过打开相对路径名所在的目录获得的
3. path参数指定了相对路径名, fd参数具有特殊值AT_FDCWD. 这种情况下, 路径名在当前工作目录中获取, openat与open相当

openat函数解决了:
1. 让线程可以使用相对路径名打开目录中的文件, 而不是只能打开当前的工作目录
2. 可以避免time-of-check-to-time-of-use(TOCTTOU).
TOCTTOU : 如果有两个基于文件的函数调用, 第二个文件调用的是第一个文件的结果, 那么第二个文件是脆弱的.

close();
关闭一个文件时还会释放进程加在这个文件上所有的记录锁.
当一进程结束的时候, 内核也会自动关闭它所有的打开的文件. 所以, close()可以不显示的调用.

记录锁 : 当一个进程正在读取,修改文件的某一区域时, 它可以阻止其他进程修改同一区域.
相关记录, 例子可以看相关博客 : http://blog.csdn.net/wudizuijimo/article/details/5257217

lseek();
指定文件的偏移量.
偏移量可能会造成空洞, 空洞不消耗磁盘空间.也可能比统一内容的文件更加节省磁盘空间.

空洞用处

  1. 下载时如果没有空洞文件,多线程下载时文件就只能从一个地方写入,这就不能发挥多线程的作用了.如果有了空洞文件,可以从不同的地址写入,就完成了多线程的优势.
  2. 开发过程中有时候需要为某个文件迅速地分配固定大小的磁盘空间
    a. 可以让文件尽可能的占用连续的磁盘扇区,减少后续写入和读取文件时的磁盘寻道开销;
    b. 迅速占用磁盘空间,防止使用过程中所需要空间不足;
    c. 后面追加数据的话,不会需要改变文件大小,所以后面将部设计metadata的修改.

I/O效率:

大多数文件系统为改善性能都采用预读技术.

预读技术:
当检测到正在进行顺序读取时, 系统就试图读取比应用要求更多的数据.预读的效果在超过一定的数据长度基本不改变, 所以时间与每次缓存的长度关系就不大了.
第一运行文件时, 系统会大量进行缓存, 所以之后的读取就会很快速, 第一会可能的慢一点

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第1章 UNIX操作系统概述 7 1.1 UNIX操作系统简介 7 1.2 UNIX系统组成 7 1.3 UNIX启动过程 8 1.4 UNIX用户登录过程 8 1.5 与UNIX有关的几个名词 9 第2章 UNIX基本常识 11 2.1 启动终端 11 2.2 登录 11 2.3 初始化文件 11 2.4 注销(退出UNIX系统) 12 第3章 UNIX文件系统 13 3.1 文件系统分类 13 3.2 文件类型 13 3.2.1 正规文件 13 3.2.2 目录文件 14 3.2.3 字符和块设备文件 15 3.2.4 套接字文件 15 3.2.5 命名管道文件 16 3.2.6 链接文件 16 3.3 树型目录结构 16 3.4 文件和目录的命名 18 3.5 UNIX文件存取权限 18 3.6 重定向与管道 21 3.6.1 UNIX重定向 21 3.6.2 UNIX管道 22 3.7 常用配置文件 22 3.7.1 /etc/passwd文件 22 3.7.2 /etc/group文件 22 3.7.3 /etc/hosts 23 3.7.4 /etc/services 23 3.8 文件系统管理 23 3.8.1 mount 23 3.8.2 umount 24 3.8.3 加载配置文件 24 3.8.4 fsck 25 第4章 UNX系统常用命令 27 4.1 UNIX命令基础 27 4.1.1 UNIX命令的一般格式 27 4.1.2 特殊功能键和字符 28 4.1.3 查看帮助信息 30 4.1.4 在后台运行程序 31 4.1.5 在指定时间执行指定程序 31 4.2 vi编辑器的最基本用法 32 4.2.1 vi简介 32 4.2.2 vi的启动和退出 33 4.2.3 vi的两种模式 33 4.2.4 vi的基本操作 34 4.2.5 vi的高级操作 36 4.3 目录操作命令 38 4.3.1 pwd 38 4.3.2 mkdir 38 4.3.3 cd 38 4.3.4 rmdir 39 4.4 文件操作命令 39 4.4.1 ls 39 4.4.2 cat 40 4.4.3 head 41 4.4.4 tail 41 4.4.5 more 41 4.4.6 cp 43 4.4.7 mv 44 4.4.8 rm 44 4.4.9 chmod 44 4.4.10 chown 46 4.4.11 chgrp 46 4.4.12 cmp 46 4.4.13 diff 47 4.4.14 wc 47 4.4.15 split 47 4.4.16 touch 48 4.4.17 file 48 4.4.18 pack 48 4.4.19 pcat 49 4.4.20 unpack 49 4.4.21 find 49 4.4.22 grep 51 4.4.23 pg 52 4.4.24 ln 52 4.4.25 sort 53 4.4.26 compress 53 4.4.27 uncompress 54 4.4.28 gzip 54 4.4.29 gunzip 54 4.4.30 tar 54 4.4.31 cpio 55 4.4.32 tee 56 4.5 状态信息命令 57 4.5.1 w 57 4.5.2 who 57 4.5.3 whodo 57 4.5.4 logname 58 4.5.5 whoami 58 4.5.6 whereis 58 4.5.7 which 58 4.5.8 date 58 4.5.9 cal 59 4.5.10 time 59 4.5.11 id 59 4.5.12 hostid 60 4.5.13 hostname 60 4.5.14 df 60 4.5.15 du 60 4.5.16 stty 61 4.5.17 tty 61 4.5.18 history 61 4.5.19 alias 61 4.5.20 echo 62 4.5.21 uname 62 4.5.22 clear 62 4.6 网络命令 62 4.6.1 arp 62 4.6.2 finger 63 4.6.3 wall 63 4.6.4 mesg 63 4.6.5 write 63 4.6.6 ping 63 4.6.7 netstat 64 4.6.8 telnet 64 4.6.9 ftp 64 4.7 进程管理命令 65 4.7.1 kill 65 4.7.2 ps 66 4.7.3 sleep 68 4.7.4 nice 68 4.7.5 shutdown 68 4.7.6 halt 69 4.7.7 poweroff 69 4.7.8 reboot 69 4.8 用户管理命令 69 4.8.1 su 69 4.8.2 groupadd 69 4.8.3 groupdel 70 4.8.4 useradd 70 4.8.5 userdel 70 4.8.6 passwd 71 第5章 shell的基础知识 72 5.1 什么是shell 72 5.2 别名化 73 5.3 shell变量 74 5.3.1 shell变量的存储机制 74 5.3.2 变量替换 74 5.3.3 命令替换 76 5.4 用户环境 76 5.5 两个重要的环境变量 77 5.6 shell启动文件 78 5.7 监控进程 78 第6章 附录 79 6.1 ftp命令参考 79 6.2 vi命令参考 80 6.3 find命令详解 85 6.3.1 Find命令形式 85 6.3.2 Find命令参数 85 6.3.3 Find命令举例 87 6.4 grep命令详解 99

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值