Linux系统编程
标准I/O
input&output 它是一切实现的基础
stdio 标准 IO sysio 系统ui
文件IO:
标准IO:优点是可移植性高,缺点是性能比系统 IO 差,且功能没有系统 IO 丰富。
系统IO:因为是内核直接提供的系统调用函数,所以性能比标准 IO 高,但是可移植性比标准 IO 差
如果一个问题的解决需要用到IO
,而且这个时候标准IO
和系统IO
都能解决问题的时候,我们优先使用标准IO
;
原因 : 移植性好、合并系统调用
虚拟地址空间
内核区:内核区是受保护的,用户不能对其进行读写操作,否则会引发段错误。
用户区 :用户可以直接进行操作
进程和程序的区别:程序是我们正在运行中的代码 ,进程在运行中会加载到内存里。
MMU 虚拟内存管理 :虚拟内存中的数据都会被MMU映射到实际中的物理内存中
虚拟地址空间在不同机器上的大小 :32位机器的大小是 2^32 64位机器的大小是2^48
文件描述符
linux系统一切皆文件
静态库的制作
main.cpp
#include"test.h"
using namespace std;
const int N = 1010;
struct student{
int age;
string name;
int sorce;
string gender;
}a[N];
int n;
int main()
{
cout<<add(1,2)<<endl;
}
test.cpp
#include"test.h"
int add(int x,int y){
return x+y;
}
test.h
#include<iostream>
#include<algorithm>
int add(int x,int y);
第一步 : 生成 .o 文件
g++ -c main.cpp test.cpp
gcc -o main.c test.c
会生成 main.o
和test.o
两个文件,并不是可执行文件,所以并不会链接,不需要使用-o
参数。
第二步 生成 .a 并使用
ar -r lq.a main.o test.o
ar rcs lq.a main.o test.o
加载a库,即用gcc命令生成目标文件时指明静态库名,gcc将会从静态库中将公用函数连接到目标文件中。注意,gcc会在静态库名前加上前缀lib,然后追加扩展名.a得到的静态库文件名来查找静态库文件。
使用方式:
g++ -o main main.cpp main.o test.o lq.a
或者
g++ -o main main.cpp -L. -ltest
动态库的制作
在
test.h
文件中定义所需的函数和类。确保包含了合适的头文件保护(#ifndef
、#define
、#endif
)。将
test.cpp
编译为动态库。在命令行或终端中执行以下命令:shellCopy Codeg++ -shared -fPIC -o libtest.so test.cpp
这将编译
test.cpp
并生成名为libtest.so
的动态库文件。在
main.cpp
文件中使用test.h
中定义的函数或类。确保在头部加入#include "test.h"
。编译
main.cpp
并链接到生成的动态库。在命令行或终端中执行以下命令:shellCopy Codeg++ -o main main.cpp -L. -ltest
这将编译
main.cpp
并链接到动态库libtest.so
。选项-L.
告诉编译器在当前目录搜索动态库,而-ltest
指定链接到名为libtest.so
的库。运行生成的可执行文件。在命令行或终端中执行以下命令:
shellCopy Code./main
这样就可以将 test.cpp
编译为动态库,并在 main.cpp
中成功调用它了
在不同文件夹下制作动态库
如果您的源文件(main.cpp
、test.cpp
、test.h
)不在同一个目录下,您可以采取以下方法进行编译和链接。
假设 main.cpp
位于 /path/to/main.cpp
,test.cpp
位于 /path/to/test.cpp
,test.h
位于 /path/to/test.h
。
- 编译 test.cpp:首先,您需要编译
test.cpp
并生成对象文件test.o
。使用以下命令:
g++ -c /path/to/test.cpp -o test.o -fPIC
这将编译 test.cpp
并生成 test.o
。
生成 .o文件
- 编译 main.cpp:接下来,您需要编译
main.cpp
并生成对象文件main.o
。使用以下命令:
g++ -c /path/to/main.cpp -o main.o
这将编译 main.cpp
并生成 main.o
。
生成 .so文件
- 生成动态库:然后,您可以使用以下命令将
test.o
和main.o
链接为一个动态库。假设您想要生成的动态库名为libtest.so
,使用以下命令:
g++ -shared -o libtest.so test.o main.o
这将链接 test.o
和 main.o
并生成 libtest.so
动态库文件。
编译链接
- 编译和链接可执行文件:最后,您可以使用以下命令来编译和链接可执行文件,以测试动态库的加载和调用:
g++ -o main main.o -ldl
这将编译 main.o
并链接动态库,并生成可执行文件 main
。
现在,如果一切顺利,您可以运行生成的可执行文件 ./main
,它会自动加载动态库并调用其中的函数。
请注意,在上述命令中,使用了绝对路径来引用源文件。如果在命令中使用相对路径,请确保路径是正确的,并根据实际情况进行相应的更改。
动态库加载失败的原因
未指定动态库的绝对路径
什么是生成与位置无关的代码?
因为动态库只在程序运行的时候才加载进内存,并且在内存中的位置是不确定的,如果生成的是与位置有关的库,那么在一些其他位置就无法成功运行。因此为了在不同的内存地址能够正常运行程序,需要使用fpic
静态库的制作流程
动态库的制作流程
Makefile:开始大型项目的构建
一个工程中的源文件不计其数,其按类型、功能、模块分别放在若干个目录中, Makefile 文件定义了一系列的规则来指定哪些文件需要先编译,哪些文件需要后编 译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 Makefile 文件就 像一个 Shell 脚本一样,也可以执行操作系统的命令。
◼ Makefile 带来的好处就是“自动化编译” ,一旦写好,只需要一个 make 命令,整 个工程完全自动编译,极大的提高了软件开发的效率。make 是一个命令工具,是一个 解释 Makefile 文件中指令的命令工具,一般来说,大多数的 IDE 都有这个命令, 比如 Delphi 的 make,Visual C++ 的 nmake,Linux 下 GNU 的 make
命名规则
1.文件命名 makefile 或者 Makefile
Makefile 规则 一个 Makefile 文件中可以有一个或者多个规则 目标 ...: 依赖 ... 命令(Shell 命令) ...
3.目标:最终要生成的文件(伪目标除外)
4.依赖:生成目标所需要的文件或是目标
5.命令:通过执行命令对依赖操作生成目标(命令前必须 Tab 缩进) Makefile 中的其它规则一般都是为第一条规则服务的
工作原理
命令在执行之前,需要先检查规则中的依赖是否存在
如果存在,执行命令,如果不存在,向下检查其它的规则,检查有没有一个规则是用来生成这个依赖的, 如果找到了,则执行该规则中的命令
检测更新,在执行规则中的命令时,会比较目标和依赖文件的时间 如果依赖的时间比目标的时间晚,需要重新生成目标 如果依赖的时间比目标的时间早,目标不需要更新,对应规则中的命令不需要被 执行
变量
模式匹配
总结 : makefile的其他规则都是为第一条规则服务的
案例
main.cpp
#include "test.h"
int main() {
cout<<add(2,3)<<endl;
cout<<endl;
}
test.h
#include"test.h"
int add(int x,int y){
return x>y?x:y;
}
test.cpp
#include"test.h"
int add(int x,int y){
return x>y?x:y;
}
执行Makefile
命令
app:test.cpp
g++ test.cpp main.cpp -o app
输出
3
gdb调试
什么是gdb?
GDB 是由 GNU 软件系统社区提供的调试工具,同 GCC 配套组成了一套完整的开发环 境,GDB 是 Linux 和许多类 Unix 系统中的标准开发环境。
一般来说,GDB 主要帮助你完成下面四个方面的功能:
- 启动程序,可以按照自定义的要求随心所欲的运行程序
- 可让被调试的程序在所指定的调置的断点处停住(断点可以是条件表达式)
- 当程序被停住时,可以检查此时程序中所发生的事 4. 可以改变程序,将一个 BUG 产生的影响修正从而测试其他 BUG
安装gdb
yum install -y gdb
查看当前安装的版本
gdb --version
如果安装成功则会有以下提示信息
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
开始debug
需要开始调试就首先要有一段测试代码
main.cpp
#include<iostream>
#include<cstring>
using namespace std;
int main()
{
int n;
cin>>n;
for(int i = 0;i<n;i++){
cout<<i<<' ';
}
cout<<endl;
return 0;
}
编译带有调试信息的二进制文件
g++ -g -Wall main.cpp -o main
其中test
是不带调试信息正常编译的,通过大小就可以看出区别了。
调试断点
开始进入gdb 调试状态
gdb main
在程序入口处打断点
break main
在这里需要说明的是 b 是 break的缩写,所以使用 b main
也是没问题的
运行程序
run
来到第一个断点处
next
继续运行
退出调试
quit
调试结束
总结
run r 运行程序
next n 继续运行
list 查看源代码
info b 查看断点
b break
函数执行的位置,在第几行打的断点
quit 退出
其他命令
print p 变量名/数组下标
finish / step
退出函数内部 遇到函数进入函数体
start 停在程序执行的第一行
open打开文件
函数原型
int open(const char *pathName, int flags); // 打开文件
int open(const char *pathName, int flags, mode_t mode); // 创建文件
pathname
:表示要打开的文件路径。flags
:用于指示打开文件的选项,常用的有O_RDONLY
、O_WRONLY
和O_RDWR
。
这三个选项必须有且只能有一个被指定。为什么O_RDWR!=O_RDONLY|O_WRONLY呢?Linux环境中,O_RDONLY被定义为0,O_WRONLY被定义为1,而O_RDWR却被定义为2。除了以上三个选项,Linux平台还支持更多的选项,APUE中对此也进行了介绍。
·mode:只在创建文件时需要,用于指定所创建文件的权限位(还要受到umask环境变量的影响)。
main.cpp
#include<iostream>
#include <fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<cstdio>
#include <unistd.h>
int main(int argc, char* argv[]) {
int fd = open("test.txt", O_RDONLY);
if (fd == -1) {
perror("open");//把一个描述性错误消息输出到标准错误 stderr。首先输出字符串 str,后跟一个冒号,然后是一个空格。
}
//printf("test.txt fd = %d \n", fd);
close(fd);
return 0;
}
输出:
open: No such file or directory
open 新建文件
#include<iostream>
#include <fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<cstdio>
#include <unistd.h>
int main(int argc, char* argv[]) {
int fd = open("lose.txt",O_RDWR|O_CREAT,0077);
// 以可读模式打开或若此文件不存在则创建它。使用此选项时需要提供第三个参数mode ,表示该文件的访问权限。1+4+2 = 7
// == rwx 可读可写可访问
if(fd==-1){
perror("open");
}
close(fd);
}
read、write函数
/*
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
参数:
- fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
- buf:需要读取数据存放的地方,数组的地址(传出参数)
- count:指定的数组的大小
返回值:
- 成功:
>0: 返回实际的读取到的字节数
=0:文件已经读取完了
- 失败:-1 ,并且设置errno
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
参数:
- fd:文件描述符,open得到的,通过这个文件描述符操作某个文件
- buf:要往磁盘写入的数据,数据
- count:要写的数据的实际的大小
返回值:
成功:实际写入的字节数
失败:返回-1,并设置errno
*/
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main() {
// 1.通过open打开english.txt文件
int srcfd = open("english.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};
int len = 0;
while((len = read(srcfd, buf, sizeof(buf))) > 0) {
write(destfd, buf, len);
}
// 4.关闭文件
close(destfd);
close(srcfd);
return 0;
}
lessk
off_t lseek(int fd, off_t offset, int whence);
第一个参数是文件描述符;第二个参数是偏移量,int型的数,正数是向后偏移,负数向前偏移;第三个参数是有三个选项:
1.SEEK_SET:将文件指针偏移到传入字节数处(文件头开始偏移)
2.SEEK_CUR:将文件指针偏移到当前位置加上传入字节数处;((当前位置开始偏移)
3.SEEK_END:将文件指针偏移到文件末尾加上传入字节数处(作为拓展作用,必须再执行一次写操作)
案例
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main() {
int ls = open("hello.txt",O_RDWR);
if(ls==-1){
perror("open");
return-1;
}
int ret = lseek(ls,0,SEEK_END); // 获取文件的长度
if(ret==-1){
perror("lseek");
return -1;
}
write(ls," ", 100); // 扩展100个字节
close(ls);
return 0;
}
stat函数
int stat(const char *file_name, struct stat *buf)
函数说明: 通过文件名filename获取文件信息,并保存在buf所指的结构体stat中
返回值: 执行成功则返回0,失败返回-1,错误代码存于errno
这些函数返回有关文件的信息,存储在指向 statbuf 的缓冲区中。对于文件本身,不需要权限,但是对于 stat()、fstatat() 和 lstat(),需要对路径名中的所有目录具有执行(搜索)权限,这些目录是指向文件的路径中的。
stat() 和 fstatat() 用于检索路径名指向的文件的信息;fstatat() 的区别如下所述。 lstat() 与 stat() 完全相同,唯一的区别是,如果路径名是符号链接,则返回有关符号链接本身的信息,而不是符号链接所指向的文件。
fstat() 与 stat() 完全相同,唯一的区别是,要检索信息的文件由文件描述符 fd 指定。
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
struct stat statbuf;
int ret = stat("a.txt",&statbuf);
if(ret==-1){
perror("stat");
return -1;
}
printf(" size = %ld ",statbuf.st_size); // 计算文件的大小
}
模拟实现 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* __name, int __type) // 判断文件是否存在
int chmod(const char *file, __mode_t mode) // 修改文件的权限 fine : 需要修改文件的路径
int truncate(const char *path, off_t length); // 修改文件大小
案例
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int ls = access("a.txt", F_OK);
if(ls==-1){
perror("access");
return -1;
}
printf("文件存在\n");
int ret = chmod("a.txt",0777);
ret == -1? perror("chmod"):printf("权限已更改!\n");
int l = truncate("a.txt",29);
l==-1? perror("truncast"):printf("大小已更改!\n");
}
输出
root@dcs-1e648dbe-0:/workspace/Linux# ls -lh
total 30K
-rwxrwxrwx 1 root root 10 Aug 8 09:13 a.txt
-rw-r--r-- 1 root root 497 Aug 8 09:13 acces.c
-rwxr-xr-x 1 root root 5 Aug 8 03:21 b.txt
-rw-r--r-- 1 root root 8 Aug 8 08:45 compile_flags.txt
-rw-r--r-- 1 root root 431 Aug 8 04:45 hello.c
-rw-r--r-- 1 root root 7.6K Aug 8 04:45 hello.txt
-rw-r--r-- 1 root root 264 Aug 8 08:46 ls-l.c
-rwxr-xr-x 1 root root 17K Aug 8 07:04 main
-rw-r--r-- 1 root root 1.1K Aug 8 03:21 main.c
-rw-r--r-- 1 root root 326 Aug 8 07:02 stat.c
root@dcs-1e648dbe-0:/workspace/Linux# ls -lh
total 30K
-rwxrwxrwx 1 root root 29 Aug 8 09:13 a.txt // 文件大小发生了改变
-rw-r--r-- 1 root root 497 Aug 8 09:13 acces.c
-rwxr-xr-x 1 root root 5 Aug 8 03:21 b.txt
-rw-r--r-- 1 root root 8 Aug 8 08:45 compile_flags.txt
-rw-r--r-- 1 root root 431 Aug 8 04:45 hello.c
-rw-r--r-- 1 root root 7.6K Aug 8 04:45 hello.txt
-rw-r--r-- 1 root root 264 Aug 8 08:46 ls-l.c
-rwxr-xr-x 1 root root 17K Aug 8 07:04 main
-rw-r--r-- 1 root root 1.1K Aug 8 03:21 main.c
-rw-r--r-- 1 root root 326 Aug 8 07:02 stat.c
root@dcs-1e648dbe-0:/workspace/Linux#
目录属性操作函数
int mkdir(const char* __path, __mode_t __mode) // 新建文件夹的名字 所授予的权限
int chdir(const char * path) // 修改进程运行时的工作目录
char*getbuf(const char*path,size_t size) // 获取当前工作目录
案例
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
int ref = rename("abc","鸡你太美");
ref==-1? perror("mkdir"):printf("修改成功\n");
char buf[128] = {0};
getcwd(buf, sizeof(buf));
printf("当前的工作目录是: %s\n", buf);
int ret = chdir("/workspace/Linux/abc");
ret == -1 ? perror("chdir") : printf("修改成功!\n");
int fd = open("c.txt", O_CREAT | O_RDONLY, 0664);
fd == -1 ? perror("open") : printf("新文件创建成功!\n");
getcwd(buf, sizeof(buf));
printf("当前的工作目录是: %s\n", buf);
close(fd);
}
首先,代码尝试将文件"abc"重命名为"鸡你太美",使用rename函数来实现。如果重命名失败,它会打印错误消息并退出程序。如果重命名成功,它会打印"修改成功"的消息。
接下来,代码获取当前的工作目录,并使用getcwd函数将其存储在名为buf的字符数组中。然后,它使用printf函数输出当前的工作目录。
接着,代码使用chdir函数将当前的工作目录修改为"/workspace/Linux/abc"。如果修改失败,它会打印错误消息并退出程序。如果修改成功,它会打印"修改成功"的消息。
然后,代码使用open函数创建一个名为"c.txt"的新文件,打开模式为O_CREAT(创建新文件)和O_RDONLY(只读模式)。如果创建新文件失败,它会打印错误消息并退出程序。如果创建新文件成功,它会打印"新文件创建成功"的消息。
最后,代码再次获取当前的工作目录,并使用close函数关闭文件描述符fd
目录遍历函数
dup,dup2函数
函数定义
int dup(int oldfd) // 复制一个新的文件描述符
int dup2(int oldfd, int newfd) // 重定向描述符
dup2与dup区别是dup2可以用参数newfd指定新文件描述符的数值。若参数newfd已经被程序使用,则系统就会将newfd所指的文件关闭,若newfd等于oldfd,则返回newfd,而不关闭newfd所指的文件。dup2所复制的文件描述符与原来的文件描述符共享各种文件状态。共享所有的锁定,读写位置和各项权限或flags等
dup.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
int main(int argc, char *argv[]) {
int fd = open("a.txt",O_RDWR|O_CREAT,0664);
if(fd==-1){
perror("open");
exit(-1);
}
int fde = dup(fd);
if(fde == -1){
perror("dup");
return -1;
}
printf("%d %d ",fd,fde);
const char*t = "world";
int ret = write(fd, t,strlen(t));
if(ret==-1){
perror("write");
return -1;
}
close(fd);
close(fde);
}
dup2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
int main() {
int fd = open("test.txt",O_RDWR|O_CREAT,0664);
if(fd==-1){
perror("open");
exit(-1);
}
int fd1 = open("test1.txt",O_RDWR|O_CREAT,0664);
if(fd1==-1){
perror("open");
exit(-1);
}
printf("%d %d \n",fd,fd1); // 3 4
int fd2 = dup2(fd, fd1); // 返回新的描述符
if(fd2==-1){
perror("open");
exit(-1);
}
printf("%d %d %d \n",fd,fd1,fd2); // 输出 3 4 4
}
fcntl函数
函数声明
fcntl(int fd, int cmd, ...)
返回值:
正确返回值根据命令码而定,错误返回-1。
/*
#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 表示追加数据
NONBLOK 设置成非阻塞
阻塞和非阻塞:描述的是函数调用的行为。
*/
#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);
if(flag == -1) {
perror("fcntl");
return -1;
}
flag |= O_APPEND; // flag = flag | O_APPEND
// 修改文件描述符状态的flag,给flag加入O_APPEND这个标记
int ret = fcntl(fd, F_SETFL, flag);
if(ret == -1) {
perror("fcntl");
return -1;
}
char * str = "nihao";
write(fd, str, strlen(str));
close(fd);
return 0;
}