Linux系统编程入门(二)

1.GDB调试

GDB 是由 GNU 软件系统社区提供的调试工具,同 GCC 配套组成了一套完整的开发环境,主要有四个方面的功能:
1.启动程序,可以按照自定义的要求运行程序;
2.可让被调试的程序在所指定的调置的断点处停住;
3.当程序被停住时,可以检查此时程序中所发生的事;
4. 可以改变程序,将一个 BUG 产生的影响修正从而测试其他 BUG。

生成带有调试信息的程序

通常,在为调试而编译时,我们会()关掉编译器的优化选项(-O), 并打开调试选项(-g)。另外,-Wall在尽量不影响程序行为的情况下选项打开所有warning,也可以发现许多问题,避免一些不必要的 BUG。
gcc -g -Wall program.c -o program

-g 选项的作用是在可执行文件中加入源代码的信息,比如可执行文件中第几条机器指令对应源代码的第几行,但并不是把整个源文件嵌入到可执行文件中,所以在调试时必须保证 gdb 能找到源文件。

输入gcc -g test.c -o test生成test,输入gcc test.c -o test1生成test1,
因为test带有调试信息,所以要比test1大一些
在这里插入图片描述

GDB命令 – 启动、退出、查看代码

1.启动和退出

gdb 可执行程序
quit

2.给程序设置参数/获取设置参数

set args 10 20  //给可执行程序设置一些传入的参数,多个参数用空格隔开
show args       //获取设置的参数

3.GDB 使用帮助

help

4.查看当前文件代码

list/l (从默认位置显示)
list/l 行号 (从指定的行显示)
list/l 函数名(从指定的函数显示)

5.查看非当前文件代码

list/l 文件名:行号
list/l 文件名:函数名

6.设置显示的行数

show list/listsize
set list/listsize 行数

生成带有调试信息的程序后,
输入gdb test启动GDB调试,但此时test程序还没有运行
在这里插入图片描述
输入quitq退出gdb,返回当前终端
在这里插入图片描述

输入set args 10 20传入参数10、20,输入show args获取传入的参数
在这里插入图片描述
输入help查看gdb的命令
在这里插入图片描述
如果输入gcc test.c -o test,即不加-g,此时通过lisi指令不能读取代码,因为生成的之可执行文件test不带有代码信息;
如果把test.c文件删除,再输入gcc -g test.c -o test,此时通过lisi指令不能读取代码,因为在调试时必须保证 gdb 能找到源文件。

输入gcc -g test.c -o test,通过lisi指令读取代码,
但是代码并没有显示完整,可以继续输入list指令读取下一部分代码
在这里插入图片描述
查看当前文件代码:
输入list 行号l 行号,就可以从指定的行号显示(将行号作为中间);
输入list 函数名l 函数名,查看指定函数的代码
按回车键默认执行上一次输入
在这里插入图片描述
查看非当前文件代码:
默认查看的是main的代码
在这里插入图片描述输入list 文件名:行号可以从指定文件名的指定行号查看,如l bubble.cpp:1
输入list 文件名:函数号可以从指定文件名的指定函数查看,如l select.cpp:selectSort
在这里插入图片描述
设置显示的行数:
显示显示的行数show list/listsize
设置显示的行数set list/listsize 行数
在这里插入图片描述

GDB命令 – 断点操作

1.设置断点

b/break 行号
b/break 函数名
b/break 文件名:行号
b/break 文件名:函数

2.查看断点

i/info b/break

3.删除断点

d/del/delete 断点编号

4.设置断点无效

dis/disable 断点编号

5.设置断点生效

ena/enable 断点编号

6.设置条件断点(一般用在循环的位置)

b/break 10 if i==5

操作和启动、查看等类似

GDB命令 – 调试命令

1.运行GDB程序

start       //程序停在第一行
run         //遇到断点才停

2.继续运行,到下一个断点停

c/continue

3.向下执行一行代码(不会进入函数体)

n/next

4.变量操作

p/print 变量名   //打印变量值
ptype 变量名     //打印变量类型

5.向下单步调试(遇到函数进入函数体)

s/step
finish      //跳出函数体

6.自动变量操作

display 变量名       //自动打印指定变量的值
i/info display      //显示设置的自动变量
undisplay 编号       //取消自动变量

7.其它操作

set var 变量名=变量值       //循环中用的较多
until                     //跳出循环

2.文件IO

标准C库的IO函数可以跨平台,即跨操作系统,效率更高
标准C库的IO函数和Linux系统IO函数属于调用和被调用的关系,
比如在Linux系统调用了标准C库的fopen函数打开文件,在底层中会先调用Linux系统的API(open函数)

标准C库IO函数和Linux系统IO函数

用标准C库函数调用fopen函数打开hello.txt文件:
调用fopen函数会返回FILE *fp(file文件指针),file文件指针其实就是个结构体,包含三部分:文件描述符、文件读写指针、I/O缓冲区

文件描述符是一个整数值,用来指向一个已经打开的文件,起到定位文件的作用;
文件读写指针是维护两个指针用来读、写数据,对文件进行操作;
I/O缓冲区提供存储数据的缓冲区(大小默认为8192byte,约8k),当刷新缓冲区、缓冲区满、正常关闭文件时将缓冲区的数据写入磁盘中,降低磁盘写入的频率,提高程序执行的效率
在这里插入图片描述
标准C库IO函数带有缓冲区,Linux系统IO函数没有缓冲区,调用一次write就会写一次磁盘,调用一个read就会读取一次磁盘。

  char *_IO_read_ptr;	/* Current read pointer */        //读指针
  char *_IO_read_end;	/* End of get area. */            
  char *_IO_read_base;	/* Start of putback+get area. */
  char *_IO_write_base;	/* Start of put area. */          //写指针
  char *_IO_write_ptr;	/* Current put pointer. */
  char *_IO_write_end;	/* End of put area. */
  char *_IO_buf_base;	/* Start of reserve area. */      //缓冲区起始位置
  char *_IO_buf_end;	/* End of reserve area. */        //缓冲区结束位置

  /* The following fields are used to support backing up and undo. */
  char *_IO_save_base; /* Pointer to start of non-current get area. */
  char *_IO_backup_base;  /* Pointer to first valid character of backup area */
  char *_IO_save_end; /* Pointer to end of non-current get area. */

  struct _IO_marker *_markers;

  struct _IO_FILE *_chain;

  int _fileno;                                           //文件描述符,Linux系统IO函数返回的文件描述符的值赋给fileno

虚拟地址空间

虚拟地址空间并不存在
在可执行程序运行期间(一个进程)就会对应一个虚拟地址空间,
以32位计算机为例,虚拟空间大小为232,约4G,其中0-3G为用户区,3-4G为内核区

0-3G用户区的空间可以让用户操作,如NULL、nullptr等存储在受保护的地址;
3-4G内核区的空间只能通过系统调用API进行操作。

可执行文件由操作系统加载到内存中,交由CPU去执行,虚拟地址空间里的数据会被CPU中的MMU(内存管理单元)映射到真实内存上,MMU能够将虚拟地址和物理地址进行转换。
在这里插入图片描述
虚拟地址空间

文件描述符

程序或可执行程序不占用内存空间、只占用磁盘空间;
当程序要运行时,操作系统就会为程序分配资源,创建一个进程,占用内存空间。

在english.txt中有一些数据,当运行test可执行程序后,操作系统会生成一个进程,这个进程就能向english中读写一些数据,在标准C的IO函数中就是调用fopen打开文件、调用fread、fwirte读写数据
在这里插入图片描述
为什么在程序中调用fopen或fread就能找到磁盘上的english.txt?
因为IO函数中封装的有文件描述符,定位要操作的文件,存储在虚拟地址空间的内核区。
在这里插入图片描述
通过PCB进程控制块中的文件描述符表(右图,数组,默认大小为1024)去存储文件描述符,使得进程能够同时打开多个文件。

文件描述符表前三个默认为标准输入、标准输出、标准错误,默认打开,对应当前的终端

不同的文件描述符可以对应同一个文件,比如调用多个fopen打开同一个文件,这多个fopen返回的文件描述符是不一样的

Linux系统IO函数

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int close(int fd);
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
off_t lseek(int fd, off_t offset, int whence);
int stat(const char *pathname, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);

int open(const char *pathname, int flags);:

//打开一个存在的文件
int open(const char *pathname, int flags);
    参数:
        - pathname: 要打开的文件路径
        - flags: 对文件的操作权限设置及其他设置
        - flgas的值:O_RDONLY, O_WRONLY, O_RDWR   必选, 这三个设置互斥
    返回值:返回一个新的文件描述符,如果调用失败,返回-1


errno: 属于Linux系统函数库,库里面的一个全局变量,记录的是最近的错误号

#include <stdio.h>
void perror(const char *s);  作用:打印errno对应的错误描述
     s参数:用户描述,比如hello,最终输出的内容是 hello:xxx(xxx为实际的错误描述)

例:
int fd = open("a.txt",O_RDONLY);   //这里写的是相对路径

int open(const char *pathname, int flags, mode_t mode);:

//创建一个新的文件
int open(const char *pathname, int flags, mode_t mode);
    参数:
        - pathname: 要打开的文件路径
        - flags: 对文件的操作权限设置及其他设置
        - flgas的值:O_RDONLY, O_WRONLY, O_RDWR   必选,这三个设置互斥
                     O_CREAT(文件若不存在则创建新文件)   可选
        - mode:八进制的数,表示用户对创建出的新的文件的操作权限,比如0777 
                最终的权限是:mode & ~umask,比如0777 & ~(0002)= 0777 & 0775 = 0111111111 & 0111111101 = 0111111101 = 0775
                             umask的作用就是抹去某些权限,可以自己设置
        返回值:返回一个新的文件描述符,如果调用失败,返回-1

例:
int fd = open("a.txt",O_RDWR | O_CREAT,0777);

在文件目录中前面都对应着操作权限,总共有十个字符,第一个字符表示文件类型,后面九个字符表示权限,三个一组,
前三个表示当前用户对文件的权限,中间三个表示当前用户所在组队文件的权限,最后三个表示其他组对文件的权限
比如open.*的权限写成8进制就是0775(0111111101),open.c的权限写成8进制就是0664(0110110100)
在这里插入图片描述
int close(int fd);:
传入参数为要关闭文件的文件描述符

ssize_t read(int fd, void *buf, size_t count);:

//read函数找到文件描述符对应的文件进行读取数据,将读取的数据向buf中保存,buf的大小由count指定
#include <unistd.h>
ssize()_t read(int fd, void *buf, size_t count);
    参数:
        - fd:文件描述符,通过open得到,通过文件描述符操作某个文件
         - buf:将读取的数据存放的地方,数组的地址  (传出参数)
         - count:指定的数组大小 
    返回值:读取成功: >0,返回实际的读取到的字节数;=0,文件已经读取完
            读取失败: -1,并且设置errno

ssize_t write(int fd, const void *buf, size_t count);

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
    参数:
         - fd:文件描述符,通过open得到,通过文件描述符操作某个文件
         - buf:要向磁盘写入的数据,数组的地址
         - count:要写入的数据实际大小
    返回值:写入成功:返回实际写入的字节数
           写入失败:返回-1,并设置errno

read和wirte示例:

int main() {
    //1.通过open打开a.txt文件
    int srcfd = open("a.txt", O_RDONLY);  //文件描述符,传入相对路径,只读
    if(srcfd == -1) {                     //打开失败,打印错误信息
        perror("open");
        return -1;
    }
    //2.创建一个新的文件(拷贝文件)
    int destfd = open("cpy.txt",O_WRONLY | O_CREAT, 0664);
    if(destfd == -1) {
        perror("open");
        return -1;
    }
    //3.读写操作
    char buf[1024] = {0};                                //定义字符数组,大小为1024,初始化为0
    int len = 0;
    //从源文件的文件描述符读取数据,保存到buf数组,数组大小为buf数组大小
    while((len = read(srcfd, buf, sizefof(buf))) > 0) {  //数据读取成功且没有读完
        write(destfd, buf, len);                         //每读一次就向cpy中写一次
    }
    //4.关闭文件
    clsoe(destfd);
    close(srcfd);

    return 0;

}

off_t lseek(int fd, off_t offset, int whence);

标准C库的函数
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);

Linux系统函数
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
      参数:
           - fd:文件描述符,通过open得到,通过文件描述符操作某个文件
           - offset:偏移量
           - whence:有三个值:SEEK_SET  设置文件指针的偏移量
                              SEEK_CUR  设置偏移量:当前位置 + 第二个参数offset的值
                              SEEK_END  设置偏移量:文件大小 + 第二个参数offset的值
        返回值:返回文件指针的位置
        作用:1.移动文件指针到头文件   lseek(fd, 0, SEEK_SET);
             2.获取当前文件指针的位置  lseek(fd, 0, SEEK_CUR);
             3.获取文件长度           lseek(fd, 0, SEEK_END);
             4.拓展文件的长度,当前文件10b,110b,增加了110个字节  lseek(fd, 100, SEEK_END);   //需要写一次数据才能扩展

例:
int ret = lseek(fd, 100 ,SEEK_END);
write(fd, " ", 1);

int stat(const char *pathname, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *statbuf);
    作用:获取一个文件的相关信息
    参数:
         - pathname:要操作文件的路径
         - statbuf:结构体变量,传出参数,用于保存获取到的文件的信息
    返回值:获取成功返回0;获取失败返回-1,并设置errno
int lstat(const char *pathname, struct stat *statbuf);
    作用:获取软连接文件的相关信息   //ln -s a.txt b.txt,设置软连接b.txt->a.txt
    参数:同上
    返回值:获取成功返回0;获取失败返回-1,并设置errno

示例:

int main() {

    struct stat statbuf;                    //定义结构体地址
    int ret = stat("a.txt", &statbuf);      //获取a.txt的信息
    if(ret == -1) {                         //获取失败
        perror("stat");
        return -1;
    }
    printf("size:%ld\n",statbuf.st_size);   //获取文件大小
    return 0;
}

在这里插入图片描述
其中mode_t类型的st_mode是16位的整数,通过标志位来记录文件类型和存储的权限
0-2位表示其他用户对文件的权限,最小位x表示可执行权限,例如S_IRWXO为00007,转换为二进制为111,表示其他用户对文件有读、写、可执行权限

3-5位表示当前组对文件的权限,6-8位表示当前用户对文件的权限,9-11为为特殊权限位

15-12位记录文件类型用一个或两个标志位标志一种文件类型,例如S_IFSOCK为0140000,转换为二进制为1100 0000 0000 0000,即15位和14位为1,通过两个标志位表示套接字文件类型

在这里插入图片描述

案例

通过ls -l可以列出当前目录下文件的信息(文件权限、硬链接数、所有者、所在组、最后修改时间、文件名称)
或通过ls -l 文件名列出某个具体文件的信息
在这里插入图片描述

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <string.h>

// 模拟实现 ls -l 指令
// -rw-rw-r-- 1 nowcoder nowcoder 12 12月  3 15:48 a.txt
int main(int argc, char * argv[]) {

    // 判断输入的参数是否正确
    if(argc < 2) {
        printf("%s filename\n", argv[0]);
        return -1;
    }

    // 通过stat函数获取用户传入的文件的信息
    struct stat st;
    int ret = stat(argv[1], &st);
    if(ret == -1) {
        perror("stat");
        return -1;
    }

    // 获取文件类型和文件权限
    char perms[11] = {0};   // 用于保存文件类型和文件权限的字符串

    switch(st.st_mode & S_IFMT) {
        case S_IFLNK:
            perms[0] = 'l';
            break;
        case S_IFDIR:
            perms[0] = 'd';
            break;
        case S_IFREG:
            perms[0] = '-';
            break; 
        case S_IFBLK:
            perms[0] = 'b';
            break; 
        case S_IFCHR:
            perms[0] = 'c';
            break; 
        case S_IFSOCK:
            perms[0] = 's';
            break;
        case S_IFIFO:
            perms[0] = 'p';
            break;
        default:
            perms[0] = '?';
            break;
    }

    // 判断文件的访问权限

    // 文件所有者
    perms[1] = (st.st_mode & S_IRUSR) ? 'r' : '-';
    perms[2] = (st.st_mode & S_IWUSR) ? 'w' : '-';
    perms[3] = (st.st_mode & S_IXUSR) ? 'x' : '-';

    // 文件所在组
    perms[4] = (st.st_mode & S_IRGRP) ? 'r' : '-';
    perms[5] = (st.st_mode & S_IWGRP) ? 'w' : '-';
    perms[6] = (st.st_mode & S_IXGRP) ? 'x' : '-';

    // 其他人
    perms[7] = (st.st_mode & S_IROTH) ? 'r' : '-';
    perms[8] = (st.st_mode & S_IWOTH) ? 'w' : '-';
    perms[9] = (st.st_mode & S_IXOTH) ? 'x' : '-';

    // 硬连接数
    int linkNum = st.st_nlink;

    // 文件所有者
    char * fileUser = getpwuid(st.st_uid)->pw_name;

    // 文件所在组
    char * fileGrp = getgrgid(st.st_gid)->gr_name;

    // 文件大小
    long int fileSize = st.st_size;

    // 获取修改的时间
    char * time = ctime(&st.st_mtime);

    char mtime[512] = {0};
    strncpy(mtime, time, strlen(time) - 1);

    char buf[1024];
    sprintf(buf, "%s %d %s %s %ld %s %s", perms, linkNum, fileUser, fileGrp, fileSize, mtime, argv[1]);

    printf("%s\n", buf);

    return 0;
}

文件属性操作函数

int access(const char *pathname, int mode);             //判断文件权限或判断文件是否存在
int chmod(const char *filename, int mode);              //修改文件权限
int chown(const char *path, uid_t owner, gid_t group);  //修改文件所有者或文件所在组
int truncate(const char *path, off_t length);           //缩减或扩展文件大小

int access(const char *pathname, int mode);

#include <unistd.h>
int access(const char *pathname, int mode);
    作用:判断某个文件是否有某个权限,或者判断文件是否存在
    参数:
         - pathname:要判断的文件的路径
         - mode:R_OK:判断是否有读权限
                 W_OK:判断是否有写权限
                 X_OK:判断是否有可执行权限
                 F_OK:判断文件是否存在
    返回值:成功返回0,失败返回-1

int chmod(const char *filename, int mode);

#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
    作用:修改文件的权限
    参数:
         - pathname:需要修改的文件的路径
         - mode: 需要修改的权限值,八进制数
    返回值:成功返回0,失败返回-1

可以通过man 2 chmod进入chmod文档查看需要指定的参数,也可以指定八进制的值
在这里插入图片描述
示例:
当前a.txt的文件权限为-rw-rw-r--,更改后权限为rwxrwxrwx
在这里插入图片描述

#include <sys/stat.h>
#include <stdio.h>
int main() {

    int ret = chmod("a.txt", 0777);

    if(ret == -1) {
        perror("chmod");
        return -1;
    }

    return 0;
}

int chown(const char *path, uid_t owner, gid_t group);

#include <unistd.h>
int chown(const char *pathname, uid_t owner, gid_t group);
    作用:修改文件所有者或文件所在组
    参数:
         - pathname:需要修改的文件的路径
         - owner:要修改的文件所有者的ID
         - group:要修改的所在组的ID
    返回值:成功返回0,失败返回-1

可以输入vim /etc/passwd查看用户ID,输入vim /etc/group查看组的ID
或者输入useradd 用户名创建新用户,id 用户名查看创建的新用户信息

int truncate(const char *path, off_t length);

#include <unistd.h>
#include <sys/types.h>
int truncate(const char *path, off_t length);
    作用:缩减或者扩展文件的尺寸至指定的大小
    参数:
        - path: 需要修改的文件的路径
        - length: 需要最终文件变成的大小
    返回值:成功返回0, 失败返回-1

示例:

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>

int main() {

    int ret = truncate("b.txt", 5);
    return 0;
}

在这里插入图片描述

目录操作函数

int mkdir(const char *pathname, mode_t mode);         //相当于mkdir,创建目录
int rmdir(const char *pathname);                      //删除空文件,只能删除空目录
int rename(const char *oldpath, const char *newpath); //对目录重命名
int chdir(const char *path);                          //更改当前进程的目录
char *getcwd(char *buf, size_t size);                 //相当于pwd,获取当前路径

int mkdir(const char *pathname, mode_t mode);

#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname, mode_t mode);
    作用:创建一个目录
    参数:
        - athname: 创建的目录的路径
        - mode: 权限,八进制的数
    返回值:成功返回0, 失败返回-1

示例:

#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>

int main() {

    // 获取当前的工作目录
    char buf[128];
    getcwd(buf, sizeof(buf));
    printf("当前的工作目录是:%s\n", buf);

    // 修改工作目录
    int ret = chdir("/home/stateabc/Linux/lesson13");

    // 创建一个新的文件
    int fd = open("chdir.txt", O_CREAT | O_RDWR, 0664);
    close(fd);

    // 获取当前的工作目录
    char buf1[128];
    getcwd(buf1, sizeof(buf1));
    printf("当前的工作目录是:%s\n", buf1);
    
    return 0;
}

在这里插入图片描述

目录遍历函数

DIR *opendir(const char *name);       //打开目录
struct dirent *readdir(DIR *dirp);    //读取目录
int closedir(DIR *dirp);              //关闭目录

DIR *opendir(const char *name);

#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
    作用:打开一个目录
    参数:
         - name: 需要打开的目录的名称
    返回值:返回DIR * 类型,理解为目录流;错误返回NULL

struct dirent *readdir(DIR *dirp);

#include <dirent.h>
struct dirent *readdir(DIR *dirp);
    作用:读取目录中的数据
    参数:
        - dirp是opendir返回的结果
    返回值:struct dirent,代表读取到的文件的信息;读取到了末尾或者失败了,返回NULL

在这里插入图片描述
int closedir(DIR *dirp);

#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);   //关闭目录

**示例:**获取目录下所有普通文件的个数

#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int getFileNum(const char * path);

// 读取某个目录下所有的普通文件的个数
int main(int argc, char * argv[]) {

    if(argc < 2) {
        printf("%s path\n", argv[0]);  //&s可执行程序 路径,可执行程序为argv[0]
        return -1;
    }

    int num = getFileNum(argv[1]);

    printf("普通文件的个数为:%d\n", num);

    return 0;
}

// 用于获取目录下所有普通文件的个数
int getFileNum(const char * path) {

    // 1.打开目录
    DIR * dir = opendir(path);

    if(dir == NULL) {
        perror("opendir");
        exit(0);
    }

    struct dirent *ptr;

    // 记录普通文件的个数
    int total = 0;

    while((ptr = readdir(dir)) != NULL) {

        // 获取名称
        char * dname = ptr->d_name;

        // 忽略掉. 和..
        if(strcmp(dname, ".") == 0 || strcmp(dname, "..") == 0) {
            continue;
        }

        // 判断是否是普通文件还是目录
        if(ptr->d_type == DT_DIR) {
            // 目录,需要继续读取这个目录
            char newpath[256];
            sprintf(newpath, "%s/%s", path, dname);
            total += getFileNum(newpath);
        }

        if(ptr->d_type == DT_REG) {
            // 普通文件
            total++;
        }


    }

    // 关闭目录
    closedir(dir);

    return total;
}

在这里插入图片描述

dup、dup2函数

int dup(int oldfd);                 //复制文件描述符
int dup2(int oldfd, int newfd);     //重定向文件描述符

int dup(int oldfd);

#include <unistd.h>
int dup(int oldfd);
    作用:复制一个新的文件描述符
    如fd=3, int fd1 = dup(fd),
    fd指向的是a.txt, fd1也是指向a.txt
    并且从空闲的文件描述符表中找一个最小的,作为新的拷贝的文件描述符

示例:

int main() {

    int fd = open("a.txt", O_RDWR | O_CREAT, 0664);
    int fd1 = dup(fd);
    printf("fd : %d , fd1 : %d\n", fd, fd1);
    close(fd);

    char * str = "hello,world";
    int ret = write(fd1, str, strlen(str));
    close(fd1);

    return 0;
}

在这里插入图片描述
int dup2(int oldfd, int newfd);

#include <unistd.h>
int dup2(int oldfd, int newfd);
    作用:重定向文件描述符
    oldfd 指向 a.txt, newfd 指向 b.txt
    调用函数成功后:newfd 和 b.txt 做close, newfd 指向了 a.txt
    oldfd 必须是一个有效的文件描述符
    oldfd和newfd值相同,相当于什么都没有做

示例:

int main() {

    int fd = open("1.txt", O_RDWR | O_CREAT, 0664);
    int fd1 = open("2.txt", O_RDWR | O_CREAT, 0664);
    printf("fd : %d, fd1 : %d\n", fd, fd1);

    int fd2 = dup2(fd, fd1);
    // 通过fd1去写数据,实际操作的是1.txt,而不是2.txt
    char * str = "hello, dup2";
    int len = write(fd1, str, strlen(str));
    printf("fd : %d, fd1 : %d, fd2 : %d\n", fd, fd1, fd2);

    close(fd);
    close(fd1);
    return 0;
}

在这里插入图片描述

fcntl函数

int fcntl(int fd, int cmd, ... /* arg */ );    //复制文件描述符
                                               //设置/获取文件的状态标志
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ...);
    参数:
    fd : 表示需要操作的文件描述符
    cmd: 表示对文件描述符进行如何操作
        - F_DUPFD : 复制文件描述符,复制的是第一个参数fd,得到一个新的文件描述符(返回值)
          例如:int ret = fcntl(fd, F_DUPFD);

        - F_GETFL : 获取指定的文件描述符文件状态flag
          获取的flag和通过open函数传递的flag是一个东西

        - F_SETFL : 设置文件描述符文件状态flag
          必选项:O_RDONLY, O_WRONLY, O_RDWR 不可以被修改
          可选性:O_APPEND, O_NONBLOCK
            O_APPEND 表示追加数据
            O_NONBLOCK 设置成非阻塞
        
        阻塞和非阻塞:描述的是函数调用的行为。

示例:

int main() {

    // 1.复制文件描述符
    // int fd = open("1.txt", O_RDONLY);
    // int ret = fcntl(fd, F_DUPFD);

    // 2.修改或者获取文件状态flag
    int fd = open("1.txt", O_RDWR);

    // 获取文件描述符状态flag
    int flag = fcntl(fd, F_GETFL);
    flag |= O_APPEND;   // flag = flag | O_APPEND

    // 修改文件描述符状态的flag,给flag加入O_APPEND这个标记
    int ret = fcntl(fd, F_SETFL, flag);

    char * str = "nihao";
    write(fd, str, strlen(str));

    close(fd);

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值