IO入门知识总结

【1】标准IO(input\output)
  1. 概念:
    标准IO是指在C库中提供的一组专门用于输入输出的函数

  2. 特点:
    不仅在UNIX系统,在很多操作系统上都实现了标准I/O库
    标准I/O库由ANSI C标准说明
    标准I/O通过缓冲机制减少系统调用,实现更高效率    
    标准I/O在系统调用函数基础上构造的,它便于用户使用
    标准IO默认打开了三个流:stdin、stdout、stderr

  3. 流:
    定义:所有的I/O操作仅是简单的从程序移进或者移出,
    这种字节流,就称为流
    
  4. FILE:(文件流指针)
    每个被使用的文件都在内存中开辟一个区域,用来存放文件
    的有关信息,这些信息是保存在一个结构体类型的变量中,
    该结构体类型是由系统定义的,取名为FILE。
    标准I/O库的所有操作都是围绕流(stream)来进行的,
    在标准I/O中,流用FILE *来描述。
使用vi -t FILE
vi -t FILE ---> ctrl+](追朔源码定义)-->struct _IO_FILE
ctrl+t:回退上一次定义

【2】缓存区:
  1.全缓存:与文件相关
    刷新缓存区的条件:
        1)程序正常退出
        2)缓存区满刷新
        3)fflush强制刷新
           fflush( FILE* 流指针);
  2.行缓存:与终端相关
    刷新缓存区的条件:
        1)\n刷新
        2)程序正常退出
        3)缓存区满刷新
        4)fflush强制刷新
  3.不缓存:没有缓存区,stderr

练习:测试标准输出缓存区的大小

1Byte*1024=1K
Byte KB MB GB
1GB = 1024MB
1MB = 1024KB
1KB = 1024Byte
【3】打开文件:
    FILE *fopen(const char *path, const char *mode);
    功能:打开文件
    参数:path:文件路径
          mode:打开文件的方式
            r:只读,文件不存在,会报错
            r+:可读可写,文件不存在,会报错
            w:只写,文件不存在创建,文件存在清空
            w+:可读可写,文件不存在创建,文件存在清空
            a:追加(可写),文件不存在创建,存在追加
            a+:可读可写,文件不存在创建,存在追加
    注:以"a"开头的附加模式下打开后续对该流操作总会写入文件末尾。
    返回值:成功:返回文件流指针
            失败:NULL
    关闭文件:
    int fclose(FILE *fp);
练习:使用读写并且不存在创建存在清空权限,打开一个文件,然后关闭这个文件。
    FILE *freopen(const char *pathname, const char *mode, FILE* fp)
    功能:将指定的文件流指针重定向到打开的文件中
    参数:path:文件路径
          mode:打开文件的方式(同上)
          fp:文件流指针
    返回值:成功:返回文件流指针
            失败:NULL    
练习:打开一个文件并将标准输出重定向到这个文件,使用printf("hello world\n");来验证
    补充:
    void perror(const char *s);
    功能:当你调用“某些”函数出错时,会将参数 s 所指的字符串和错误的原因输出到标准设备。

    char *strerror(int errnum);
    功能:根据错误号返回错误原因字符串
    参数:errnum:errno号

    int fprintf(FILE *stream, const char *format, ...);
    功能:向指定的流中输出数据
    参数:
        stream:指定的文件流
        const char *format, ...:向流输入的内容
    返回值:
        成功返回向文件流输入内容的字符个数
        失败返回0
练习1:指定一个文件,向这个文件输入hello world.
练习2:测试一个进程最多可以打开多少个文件?
main()
{
    int num=0;
    FILE *fp;
    while(1)
    {
        fp = fopen("./test.txt","r");
        if(fp == NULL)
        {
            break;
        }
        num++;
        
    }
    printf("num:%d\n",num);
}
【4】每次一个字符的读写:
    int fgetc(FILE *stream);
    功能:每次一个字符的读
    参数:stream:文件流指针
    返回值:成功:读到字符的ASCII
            失败或文件结尾:EOF(-1)
            
    int fputc(int c, FILE *stream);
    功能:向指定的文件中写一个字符
    参数:c 要写的字符
           stream  要写到的流
    返回值:成功:写入的字符;
            失败:EOF
            
练习:用fgetc和fputc实现cat的功能,从命令行输入文件名.
    
【5】每次一行的IO:
    char *fgets(char *s, int size, FILE *stream);
    功能:从指定的流中读取最多size-1个字符,第size个字符是'\0'
        存放在s所指的空间
    参数:s    读到数据的存放位置
          size    一次期望读到的数据,若遇到'\n'会直接返回
          stream    流
    返回值:成功返回s的地址
            失败或读到文件结尾:NULL

    int fputs(const char *s, FILE *stream);
    功能:向指定的文件流中写入一串字符
    参数:s:要写入的字符内容
          stream  流
    返回值: 成功 实际写入的数据的个数
             失败 EOF
    注意:
        fgets最多可以读size - 1个,最后一个字符必须是'\0'字符
        fgets函数是每次读一行的函数,遇到'\n'字符返回

        
练习1:用fgets实现wc –l的功能,文件名从命令行输入
    int line = 0;
    while(1)
    {
        fgets(buf)
        if(NULL==fgets())
            break;
        buf[strlen(buf)-1] == '\n';
            line++;
    }

练习2:用fgets和fputs实现cp的功能,两个文件名从命令行输入
        cp 文件1 文件2
        while(1)
        {
            if(fgets(buf,sizeof(buf),fp1) == NULL)
                break;
            fputs(buf,fp2)
        }

作业:
1、使用fgets从终端输入字符串,将此字符串写入指定文件里,当输入"quit"退出.

【1】直接IO(二进制IO)
    size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);//从流里读
    功能:从文件流读取多个元素
    参数:      ptr :用来存放读取元素的地址
              size :元素大小  sizeof(数据类型)
              nmemb :读取元素的个数
                stream :要读取的文件
    返回值:成功:读取的元素的个数;读到文件尾: 0
            失败: -1
            
    size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);//往流里写
    功能:按对象写
    参数:同上    
    返回值:成功:写的元素个数
            失败 :-1

    注意:
        两个函数的返回值为:读或写的对象数
        对于二进制数据我们更愿意一次读或写整个结构
练习1:从一个文件中读取数据然后将数据往另外一个文件里去写
【2】文件的偏移:
    int fseek(FILE *stream, long offset, int whence);
    功能:设定stream流的文件位置
    参数:    stream 要偏移的流指针
            offset  偏移量                                    
                    正数:向文件结尾位置移动                                                 负数:向文件开始位置
            whence  相对位置
                SEEK_SET   开始位置
                SEEK_CUR   当前位置
                SEEK_END   结尾位置
    返回值:成功:0 失败:-1
    
    long ftell(FILE *stream);
    功能:取得当前的文件位置
    参数:要检测的流
    返回值:当前的文件位置,出错则为-1L

    void rewind(FILE *stream);
    功能:设定流的文件位置指示为文件开始

    注意:rewind(fp)的功能和fseek(fp, 0, SEEK_SET)的功能相同    
练习:
将指定文件的光标向后移动10个字节单位,并且写入一个字符    
【3】文件IO:
    1.定义:
        posix(可移植操作系统接口)定义的一组函数
    2.特点:
        不带缓冲机制,每次操作都引起系统调用
        通过文件描述符来访问文件
        可以访问Linux下各种类型文件
    3.文件描述符:
        每个打开的文件都对应一个文件描述符。
        文件描述符是一个非负整数。Linux为程序中每个打开的文件分配一个文件描述符。
        文件描述符从0开始分配,依次递增。
        在文件IO中默认打开了三个文件描述符0,1,2对应标准输入、标准输出、标准出错
        3,4,5
        关闭(3)---打开后,3
【4】打开文件:
    int open(const char *pathname, int flags);
    功能:打开文件
    参数:pathname:文件路径名
          flags:打开文件的方式
            O_RDONLY:只读
            O_WRONLY:只写
            O_RDWR:可读可写
            O_CREAT:文件不存在创建
            O_TRUNC:清空文件内容
            O_APPEND:追加
    返回值:成功:文件描述符
            失败:-1
    当open函数的第二个参数指定为O_CREAT时,
    需要用下面的函数,给他指定文件权限    
    int open(const char *pathname, int flags, mode_t mode);
    
    标准IO        文件IO:
      r            O_RDONLY
      r+        O_RDWR
      w            O_WRONLY|O_CREAT|O_TRUNC,0666
      w+        O_RDWR|O_CREAT|O_TRUNC,0666
      a            O_WRONLY|O_CREAT|O_APPEND,0666
      a+        O_RDWR|O_CREAT|O_APPEND,0666
    
【5】关闭文件:    
    close(int fd);
    
【6】读写文件:    
    #include <unistd.h>
    ssize_t read(int fd, void *buf, size_t count);
    功能:从一个已打开的可读文件中读取数据
    参数:fd  文件描述符
          buf  存放位置
          count  期望的个数
    返回值:成功:实际读到的个数
              返回-1:表示出错,并设置errno号
              返回0:表示读到文件结尾    
练习:从指定文件中使用read读取文件的内容,并在终端打印出来。
              
    ssize_t write(int fd, const void *buf, size_t count);
    功能:向指定文件描述符中,写入 count个字节的数据。
    参数:fd   文件描述符
          buf   要写的内容
          count  期望值
    返回值:成功:实际写入数据的个数
               -1:失败  
    注:写要准确的个数,读可以不要准确的个数。           
练习:用文件IO实现cp功能,文件名从命令行输入
    open(argv[1],O_RDONLY)
    open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,0666);
    while(1)
    {
        if(read(buf) == 0)
        {
            printf("read success\n");
            break;
        }
        write();    
    }
【7】文件的偏移:   
    off_t lseek(int fd, off_t offset, int whence);
    功能:设定文件的偏移位置    
    参数:fd:文件描述符
        offset偏移量                  
            正数:向文件结尾位置移动
            负数:向文件开始位置
        whence  相对位置
                SEEK_SET   开始位置
                SEEK_CUR   当前位置
                SEEK_END   结尾位置
    返回值:
            off_t :long long int
            成功:文件的当前位置
            失败:-1

练习:求文件的长度。
            
练习:
    1-- 打开一个文件,不存在创建,存在清零
    2-- 向文件中第 10 位置处写一个字符,
    3-- 在文件此时的位置,后20个位置处,写一行字符串hello进去
【8】获取文件属性:
    int stat(const char *path, struct stat *buf);
    功能:获取文件属性
    参数:path:文件路径名
          buf:保存文件属性信息的结构体
    返回值:成功:0
            失败:-1
注:stat 文件名:在终端下可查看对应文件的文件属性
    struct stat {
        ino_t     st_ino;     /* inode number */
        mode_t    st_mode;    /* protection */
        nlink_t   st_nlink;   /* number of hard links */
        uid_t     st_uid;     /* user ID of owner */
        gid_t     st_gid;     /* group ID of owner */
        off_t     st_size;    /* total size, in bytes */
        time_t    st_atime;   /* time of last access */
        time_t    st_mtime;   /* time of last modification */
        time_t    st_ctime;  /* time of last status change */
    };
        
    S_IRUSR    00400   
    00400 --> 000 000 100 000 000
    if(st_mode & S_IRUSR)
        putchar('r');
    else
        putchar('-');
    结果为非0,说明有用户读权限
        为0,说明没有读权限
        
    struct group *getgrgid(gid_t gid);
    功能:将用户组id转换成组名
           struct group {
               char   *gr_name;       /* group name */
               char   *gr_passwd;     /* group password */
               gid_t   gr_gid;        /* group ID */
               char  **gr_mem;        /* group members */
           };
    struct passwd *getpwuid(uid_t uid);
    功能:将用户id转换成用户名
    char *ctime(const time_t *timep);
    功能:将时间转换成字符串
        "Wed Jun 30 21:49:08 1993\n"
       
笔记:
目录的操作:
    DIR *opendir(const char *name); "." ".."
    功能:获得目录流
    参数:要打开的目录
    返回值:成功:目录流
            失败:NULL
    man opendir观察此函数得返回值类型
    
    struct dirent *readdir(DIR *dirp);
    功能:读目录
    参数:要读的目录流
    返回值:成功读到的信息    失败NULL
    
    man readdir 观察此函数得返回值类型
    
    返回值为结构体,该结构体成员为描述该目录下的文件信息
    struct dirent {
        ino_t   d_ino;                   /* 索引节点号*/
        off_t   d_off;               /*在目录文件中的偏移*/
        unsigned short d_reclen;    /* 文件名长度*/
        unsigned char  d_type;      /* 文件类型 */
        char    d_name[256];          /* 文件名 */
    };

    int closedir(DIR *dirp);
    关闭目录流

【1】目录的操作:
    DIR *opendir(const char *name); "." ".."
    功能:获得目录流
    参数:要打开的目录
    返回值:成功:目录流
            失败:NULL

    struct dirent *readdir(DIR *dirp);
    功能:读目录
    参数:要读的目录流
    返回值:成功读到的信息    失败NULL

    返回值为结构体,该结构体成员为描述该目录下的文件信息
    struct dirent {
        ino_t   d_ino;                   /* 索引节点号*/
        off_t   d_off;               /*在目录文件中的偏移*/
        unsigned short d_reclen;    /* 文件名长度*/
        unsigned char  d_type;      /* 文件类型 */
        char    d_name[256];          /* 文件名 */
    };

    int chdir(const char *path); 将调用进程当前的工作目录改为path指定的目录。
    int closedir(DIR *dirp);
    关闭目录流
        
练习:编程实现ls的功能
【2】库
    1.定义:本质上来说库是一种可执行代码的二进制形式;
    通俗讲就是把一些常用函数的目标文件打包在一起,提供相应
    函数的接口,便于程序员使用;它可以被操作系统载入内存执行。
    由于windows和linux的本质不同,因此二者库的二进制是不兼容的
    2.库的分类:
      1)静态库:静态库在程序编译时会被链接到目标代码中,
    程序运行时将不再需要该静态库,因此体积较大。
      2)动态库在程序编译时并不会被连接到目标代码中,
      而是在程序运行时才被载入,因此在程序运行时还需要动态库
      存在,因此代码体积较小
    3.静态库的制作步骤:
        1-将源文件编译生成目标文件
            gcc -c  add.c -o add.o
        2-创建静态库用ar命令,它将很多.o转换成.a
            ar  crs  libmyadd.a  add.o
            //ar crs命令将.o文件变成库文件
            静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,扩展名为.a
        3-使用静态链接库
            gcc  main.c  -L.  -lmyadd
             -L为添加静态库的搜索路径,.为链接库当前路径
        4-执行./a.out
      优缺点:
        优点:    程序中已包含代码,运行时不再需要静态库。
                运行时无需加载库,运行速度更快。
        缺点:    静态库中的代码复制到了程序中,使程序会占更多的磁盘和内存空间
                静态库升级后,程序需要重新编译链接
    4.动态库的制作步骤:    
        1-我们用gcc来创建共享库
            gcc -fPIC  -c hello.c  -o hello.o
                -fPIC 创建与地址无关的编译程序
            gcc -shared -o libmyhello.so hello.o
            fPIC的全称Position Independent Code
        2-编译代码    
            gcc main.c  -L. -lmyhello
        3-为了让执行程序顺利找到动态库,有三种方法 :
            (1)把库拷贝到/usr/lib和/lib目录下。
            (2)在LD_LIBRARY_PATH环境变量中加上库所在路径。
                export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
                (终端关闭,环境变量就没在了)
            (3) 添加/etc/ld.so.conf.d/*.conf文件,
            把库所在的路径加到文件末尾,并执行ldconfig刷新
                sudo vi xx.conf  注意:xx为动态库名
                添加动态库存在的绝对路径,如:
                /home/linux/19072/IO/day3/
     优缺点:    
        优点:    程序在执行时加载动态库,代码体积小
                将一些程序升级变得简单。
                不同的应用程序如果调用相同的库,那么在内存
            里只需要有一份该共享库的实例。
        缺点:运行时还需要动态库的存在,移植性较差    
【3】进程:
    1.进程和程序的区别
    程序:编译好的可执行文件
          存放在磁盘上的指令和数据的有序集合(文件)
          程序是静态的,没有任何执行的概念

    进程:一个独立的可调度的任务
          执行一个程序所分配的资源的总称
          进程是程序的一次执行过程
          进程是动态的,包括创建、调度、执行和消亡
    
    2.进程包含三个段:
        1)“数据段”存放的是全局变量、常数以及动态数据分配的数据空间
    (如malloc函数取得的空间)等。
        2)“正文段”存放的是程序中的代码
        3)“堆栈段”存放的是函数的返回地址、函数的参数以及程序中的局部变量
    
    3.进程的类型:
        1)交互进程:交互进程既可以在前台运行,也可以在后台运行。
    该类进程经常与用户进行交互,需要等待用户的输入,当接收到用户的输入后,
    该类进程会立刻响应。例如:ctrl+c
        2)批处理进程:该类进程不属于某个终端,它被提交到一个队列中以便顺序执行
        3)守护进程:该类进程在后台运行的特殊进程。它一般在Linux启动时开始执行,系统关闭时才结束,守护进程名通常以d结尾。
    
    4.进程的运行状态:
        1)运行态:此时进程或者正在运行,或者准备运行。
        2)等待态:此时进程在等待一个事件的发生或某种系统资源。
        可中断:处在这种状态下的进程可以被信号中断,接收到信号或被显示地唤醒呼叫,唤醒之后,进程将转变为运行态。
        不可中断:它不会处理信号,只有在它所等待的事件发生时,进程才被显示的唤醒,
                  或者处于该状态的进程被使用wake_up()函数明确唤醒时才能转换到可运行的就绪状态。
        3)停止态:此时进程被中止。
        4)死亡态:这是一个已终止的进程,但还在进程向量数组中占有一个task_struct结构。
       D    uninterruptible sleep (usually IO)(不可中断的等待态)
       R    running or runnable (on run queue)(运行态)
       S    interruptible sleep (可中断的等待态)
       T    stopped, either by a job control signal or because it is
            being traced.(停止态)
       X    dead (should never be seen)(死亡态)
       Z    defunct ("zombie") process, terminated but not reaped by its
            parent.(僵尸态)

       For BSD formats and when the stat keyword is used, additional
       characters may be displayed:
       <    high-priority (not nice to other users)(高优先级)
       N    low-priority (nice to other users)(低优先级)
       s    is a session leader(会话组组长)
       l    is multi-threaded (线程)
       +    is in the foreground process group.(前台)
            空:表示后台
            
【4】相关命令:
    1.ps aux:
    2.top:
        shift+‘>’:向下查找
        shift+‘<’:向上查找
        q:退出
    3.nice:按用户指定的优先级运行进程
        PR=NI+20  NI [-20,19]数字越小优先级越高
        nice n ./a.out
        例如:nice 5 ./a.out 修改成优先级为5
    4.renice:改变正在运行进程的优先级
            renice 5 PID
    5.kill:发送信号(包括后台进程)
            kill  -num  PID   给指定的进程,发送一个信号。
            kill -l  查看进程信号
                2) SIGINT         停止信号,默认杀死进程。ctrl + c
                3) SIGQUIT        退出信号,默认也是杀死进程。 ctrl + \
                9) SIGKILL         杀死进程,不能被忽略,不能被捕捉
                14) SIGALRM       闹钟信号,默认也是杀死进程
                17)SIGCHLD      儿子状态改变,内核会给它的父亲发送此信号
                18) SIGCONT       唤醒信号,唤醒之后变为后台运行
                19) SIGSTOP       暂停信号, 不能忽略,不能被捕捉
                20) SIGTSTP        暂停信号,  ctrl + z
            补充:killall 进程名 :结束进程
    
    后台运行./a.out &
    6.bg  将暂停的进程在后台继续执行
        bg 工作号
    7.fg  把后台运行的进程放到前台运行
        fg 工作号
    8.jobs 查看所有的后台进程工作号:                
【5】创建进程函数:
    pid_t fork(void);
    返回值:
        失败:-1
        成功:
            0 :在子进程中
            >0: 在父进程中返回的是子进程的进程号
    特点:
    1)fork函数是用来创建进程的,fork之后产生了两个进程
    ,每个进程都会有返回值,所以父进程中返回的是子进程
    的进程号(>0);在子进程中返回0
    2)子进程几乎拷贝了父进程的全部内容。包括代码、
    数据、系统数据段中的pc值、栈中的数据、父进程中打开
    的文件等;但它们的PID、PPID是不同的。
    3)父子进程有独立的地址空间,互不影响;当在相应的
    进程中改变全局变量、静态变量,都互不影响。
    4)若父进程先结束,子进程成为孤儿进程,被init进程
    收养,子进程变成后台进程。
    5)若子进程先结束,父进程如果没有及时回收,子进程
    变成僵尸进程(要避免僵尸进程产生)

    问题:main函数调用进程函数fork时,那main是fork的父进程吗?
    答:main函数是一个入口函数,在调用fork函数时,
父子进程都拥有进入main函数的权利,故此,父子进程各调用一遍main函数。        
【6】获取进程号
    pid_t getpid(void);
    功能:获取当前进程的进程号
        pid_t getppid(void);
    功能:获取当前进程的父进程号
【6】进程退出函数
    void exit(int status);
    功能:结束进程,刷新缓存
    void _exit(int status);
    功能:结束进程,不刷新缓存
    status是一个整型的参数,可以利用这个参数传递进程
    结束时的状态。
    通常0表示正常结束;
    其他的数值表示出现了错误,进程非正常结束。
    在实际编程时,可以用wait系统调用接收子进程的返回值
    ,进行相应的处理。

【7】进程回收函数
    pid_t wait(int *status);
    功能:阻塞等待子进程退出,回收资源(回收僵尸进程)
    参数:status是一个整型指针,指向的对象用来保存
    子进程退出时的状态。
?      status若为空,表示忽略子进程退出时的状态
?      status若不为空,表示保存子进程退出时的状态
        另外,子进程的结束状态可由Linux中一些特定的宏来测定。
    返回值:成功:子进程的进程号
            失败:-1

    pid_t waitpid(pid_t pid, int *status, int options);
    功能:阻塞等待子进程退出,回收资源
    参数:pid:>0 指定子进程进程号
               =-1任意子进程
               =0 等待其组ID等于调用进程的组ID的任一子进程
               <-1等待其组ID等于pid的绝对值的任一子进程
          status:同上
          options:WNOHANG:不阻塞
                    0:阻塞
    返回值:正常:结束的子进程的进程号
              使用选项WNOHANG且没有子进程结束时:0
            出错:-1
    waitpid(-1,NULL,0) ==> wait(NULL);   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值