Linux系统编程

 一、GCC/G++

gcc xxx.c -o xxx

 二、静态库的制作和使用

 Linux中静态库的后缀为libxxx.a,windows中静态库的后缀为libxxx.lib

静态库的制作命令:

ar rcs libxxx.a xx1.o xx2.o ...

r - 将文件插入备存文件中

c - 建立备存文件

s - 索引

静态库的使用

gcc xxx.c -o xxx -I ./include -L ./lib -l xxx

三、动态库的制作和使用

Linux中动态库的后缀为libxxx.so,windows中动态库的后缀为libxxx.dll

动态库的制作:

(1)获取.o文件

gcc -c -fpic/fPIC a.c b.c ...

(2)gcc得到动态库

gcc -shared a.o b.o -o libxxx.so

使用动态库前需要先添加环境变量

方法:进入/etc/profile,将动态库的路径添加进去,并且新开终端或者手动输入命令source /etc/profile

使用动态库的方法:

gcc xxx.c -o xxx -I ./include -L ./lib -l xxx

四、MakeFile

程序编译过程

预处理

gcc -E test.c -o test.i

编译

gcc -S test.i -o test.s

汇编

gcc -c test.s -o test.o

链接

gcc test.o -o test
gcc test.c -o test

1、MakeFile变量

IMMEDIATE = DEFERRED 
IMMEDIATE ?= DEFERRED 
IMMEDIATE := IMMEDIATE 
IMMEDIATE += DEFERRED or IMMEDIATE 

“=” : 引用变量时原样展开,有点类似 C 语言里面的 define;

“?=”: 条件赋值,在之前没有赋值的情况下才给变量赋值;

“:=”: 立即赋值,在变量定义时就直接展开;

“+=”: 追加变量。

引用变量时使用$(variable)

2、自动推导规则

       在使用 make 编译.c 源文件时,编译.c 源文件规则的命令可以不用明确给出。这是因为 make 本身存在一个默认的规则,能够自动完成对.c 文件的编译并生成对应的.o 文件,它执 行命令“cc -c”来编译.c 源文件

3、预定义变量

AR : 归档维护程序的名称,默认值为 ar

CC : C 编译器的名称,默认值为 cc

CXX : C++ 编译器的名称,默认值为 g++

$@ : 目标的完整名称

$< : 第一个依赖文件的名称

$^ : 所有的依赖文件

%: 通配符,匹配一个字符串,两个%匹配的是同一个字符串

如以下

add.o:add.c
    gcc -c add.c
div.o:div.c
    gcc -c div.c
sub.o:sub.c
    gcc -c sub.c
mult.o:mult.c
    gcc -c mult.c
main.o:main.c
    gcc -c main.c

可转换为

%.o:%.c
    CC -c $< -o $@

 

 五、GDB调试

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

例如:

gcc -g -Wall test.c -o test

2、常用GDB命令

 

 

六、Linux文件I/O

 

内核会为每个进程维护一个打开文件的列表,该列表称为文件表。文件表是由一些非负整数进行索引的,这些非负整数称为文件描述符。文件描述符使用int类型表示,其范围是0~1023,当函数出错无法返回文件描述符时通常返回-1。

每个进程都至少包含三个文件描述符:0、1、2分别表示标准输入、标准输出和标准错误,在Linux的C标准库中提供了三个宏来表示,分别是:STDIN_FILENO,STDOUT_FILENO,STDERR_FILENO。

1、打开文件open()

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

int open(const char * name, int flags)
int open(const char * name, int flags, mode_t mode)

作用:打开文件,创建文件

参数:- const char * name:文件地址

- flags:是由一个或多个标志位的按或组合,支持三种访问模式:O_RDONLY(只读),O_WRONLY(只写),O_RDWR(读写)。同时flags还可以与其他的一些标志位进行按位或运算修改打开文件的行为,如O_APPEND、O_ASYNC等等,比较常用的是O_CREAT在文件不存在时创建文件。

- mode:当创建文件时需要指定mode的值,mode是常见的UNIX权限位集合,比如八进制的0644,最终写入磁盘的权限位是由mode参数和用户的文件创建掩码(umask)取反并执行按位与操作得到的。

2、关闭文件close()

#include <unistd>

int close (int fd)

作用:关闭文件

参数:- fd:文件描述符

3、create()函数

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

int creat (const char * name, mode_t mode)

作用:创建文件,和open函数添加O_CREATE标志位作用一样

参数:- name:文件名称

- mode:常见的UNIX权限位集合

4、read()函数

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t len)

作用:读取文件

参数:- fd:文件操作符,通常是open()函数的返回值。

           - buf:读取到的字节要写入的字符数组

           - len:读取到的字节的长度

返回值:执行成功会返回写入buf中的字节数,失败则返回-1,到达文件末尾时返回0.实际上read()函数有很多可能的结果:

 读取文件中的所有字节:

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

#define BUFFER_SIZE 4096

int main() {
    int fd = open("file.txt", O_RDONLY); // 打开文件,返回文件描述符
    if (fd == -1) {
        perror("open");
        return 1;
    }

    char buffer[BUFFER_SIZE];
    ssize_t bytesRead;
    ssize_t totalBytesRead = 0;

    while ((bytesRead = read(fd, buffer, sizeof(buffer))) > 0) {
        totalBytesRead += bytesRead;
        // 在这里可以对读取到的数据进行处理,比如输出到标准输出
    }

    if (bytesRead == -1) {
        perror("read");
        return 1;
    }

    close(fd); // 关闭文件

    return 0;
}

5、write()函数

#include <unistd.h>

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

参数:- fd :文件描述符

           - buf:写入的字符

           - count:写入字符的字节数

返回值:执行成功返回写入的字节数并更新文件位置,失败返回-1。

Append模式:当以Append模式打开文件描述符(参数设置O_APPEND),写操作不是从文件描述符的当前位置开始,而是从当前文件的末尾开始。这样可以保证即使存在多个进程,所有的写操作还是能够保证是追加写,比如日志更新操作。

6、lseek()函数

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

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

参数:- fd:文件描述符

           - offset:偏移量

           - whence:

                        SEEK_SET:设置文件指针的偏移量

                        SEEK_CUR:设置偏移量,当前位置 + offset

                        SEEK_END:设置偏移量,文件大小 + offset

返回值:成功返回文件指针的位置,失败返回-1

使用方法:

1)移动文件指针到文件头:lseek(fd, 0, SEEK_SET)

2)获取当前文件指针的位置:lseek(fd, 0, SEEK_CUR)

3)获取文件长度:lseek(fd,0,SEEK_END)

4)拓展文件长度,从10b到110b:lseek(fd,100,SEEK_END)

示例,将一个文件增加100b的大小:

int main() {
    int fd = open("hello.txt", O_RDWR);
    if (fd == -1) {
        perror("open");
        return -1;
    }

    int ret = lseek(fd, 100, SEEK_END);
    if (ret == -1) {
        perror("lseek");
        return -1;
    }

    //写一个空数据
    write(fd, " ", 1);

    close(fd);

    return 0;
}

7、一组stat()函数

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

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

参数:- pathname:文件路径

          - statbuff:结构体变量,传出参数,用来保存获取到的文件信息

作用:返回由参数path指定的文件的文件信息

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

参数:- pathname:文件路径

          - statbuf:结构体变量,传出参数,用来保存获取到的文件信息

作用:用于获取软连接文件的信息

int fstat(int fd, struct stat *statbuf);

参数:- pathname:文件路径

          - statbuf:结构体变量,传出参数,用来保存获取到的文件信息

作用:返回由文件描述符指定的文件信息

示例,打印一个文件的大小

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

int main() {
    struct  stat statbuf;
    int ret = stat("a.txt", &statbuf);
    if (ret == -1) {
        perror("stat");
        return -1;
    }

    printf("size:%ld\n", statbuf.st_size);

    return 0;
}

示例:自己实现“ls -l”

//模拟实现ls -l
//-rw-r--r-- 1 root root 13 Jul 11 00:11 a.txt
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>

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_uid)->gr_name;

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

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

    //输出

    char buf[1024] = {0};
    sprintf(buf, "%s %d %s %s %ld %s %s", perms, linkNum, fileUser, fileGrp, fileSize, time, argv[1]);

    printf("%s", buf);

    return 0;
}

8、chmod()函数

#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);

参数:- pathname:文件路径

           - mode:需要修改的权限

作用:修改文件的权限

示例代码:修改文件的权限

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

int main() {
    int ret = chmod("a.txt", 0775);
    if (ret == -1) {
        perror("chmod");
        return -1;
    }

    return 0;
}

9、truncate()函数

    #include <unistd.h>
    #include <sys/types.h>
    int truncate(const char *path, off_t length);

参数:- path:文件路径

          - length:最终文件的大小

作用:缩减或者扩展文件到指定尺寸

示例代码,讲文件的大小改为5

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

int main() {
    int ret =  truncate("b.txt", 5);
    if (ret == -1) {
        perror("truncate");
        return -1;
    }

    return 0;
}

10、access()函数

#include <unistd.h>
int access(const char *pathname, int mode);

参数:- pathname:文件路径

          - mode:R_OK:判断是否有读的权限

                         W_OK:判断是否有写的权限

                         X_OK:判断是否有执行的权限

                         F_OK:判断是否存在

作用:判断某个文件是否有某个权限,或者判断文件是否存在

示例代码:判断文件是否存在

#include <unistd.h>
#include <stdio.h>

int main() {

    int ret = access("a.txt",F_OK);
    if (ret == -1) {
        perror("access");
        return -1;
    }
    printf("文件存在!!\n");

    return 0;
}

11、getcwd()函数

    #include <unistd.h>
    char *getcwd(char *buf, size_t size);

参数:- buf:存储路径,指向一个数组,

           - size:数组的大小

返回值:返回指buf的指针

作用:获取当前的工作目录

12、chdir()函数

    #include <unistd.h>
    int chdir(const char *path);

参数:需要修改的工作路径

作用:修改进程的工作目录

示例,获取当前的工作路径并修改

#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/wmz/Desktop/Lesson/Lesson13");
    if (ret == -1) {
        perror("chdir");
        return -1;
    }

    //创建一个新文件
    int fd = open("chdir.txt", O_CREAT | O_RDWR, 0664);
    if (fd == -1) {
        perror("open");
        return -1;
    }

    close(fd);

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


    return 0;
}

13、opendir()函数

    #include <sys/types.h>
    #include <dirent.h>
    DIR *opendir(const char *name);

参数:要打开的目录名称

作用:打开一个目录

返回值:成功调用会返回目录dir的文件描述符,目录流,失败返回-1

14、readdir()函数

    #include <dirent.h>
    struct dirent *readdir(DIR *dirp);

参数:opendir打开的文件流

作用:读取目录中的数据

返回值:一个结构体dirent代表读取到的文件信息,读取到末尾或者失败了则返回NULL

15、closedir()函数

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

参数:opendir打开的目录流

作用:关闭opendir打开的目录流

返回值:成功时关闭目录流并返回0,失败则返回-1

示例代码,打开一个目录,并统计目录下的文件个数,然后关闭目录

#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]);
        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;
}

16、dup()函数和dup2()函数

#include <unistd.h>
int dup(int oldfd);
int dup2(int oldfd, int newfd)

参数:- oldfd:旧的文件描述符

           - newfd:新的文件描述符

 作用:dup用来复制参数oldfd所指的文件描述符,当复制成功是,返回最小的尚未被使用过的文件描述符,若有错误则返回-1

dup2可以用参数newfd指定新文件描述符的数值。若参数newfd已经被程序使用,则系统就会将newfd所指的文件关闭,若newfd等于oldfd,则返回newfd,而不关闭newfd所指的文件。

示例代码

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

int main() {
    int fd1 = open("a.txt", O_RDWR | O_CREAT, 0664);
    int fd2 = dup(fd1);
    if (fd1 == -1) {
        perror("open");
        return -1;
    }

    if (fd2 == -1) {
        perror("dup");
        return -1;
    }
    printf("fd1: %d, fd2: %d\n", fd1, fd2);
    close(fd1);

    char * str = "hello world";
    int ret = write(fd2, str, strlen(str));
    if (ret == -1) {
        perror("write");
        return -1;
    }

    close(fd2);


    return 0;
}

17、fcntl()函数

#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函数传递的是一个东西
                F_SETFL:设置文件描述符的状态flag
                    必选项:O_RDONLY,O_WRONLY,O_RDWR
                    可选项:O_APPEND,O_NONBLOCK
                        O_APPEND:表示追加数据 
                        O_NONBLOCK:设置成非阻塞
                    阻塞和非阻塞:描述函数的调用行为

 作用:根据文件描述词来操作文件的特性。

示例代码:

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

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);
    if (fd == -1) {
        perror("open");
        return -1;
    }
    //获取文件描述符的状态flag
    int flag = fcntl(fd, F_GETFL);

    flag = flag | O_APPEND;

    //修改文件描述符状态的flag,加入O_APPEND
    int ret =  fcntl(fd, F_GETFL,flag);

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

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

    close(fd);

    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值