一、linux编程入门
1、库文件
库文件分为静态库和动态库
区别:静态库在程序链接的阶段被复制到程序中;动态库在链接阶段没有复制到程序中,在程序运行时系统动态的加载到内存中。
静态库
静态库的制作
命名规则:
Linux下 : libxxx.a
lib : 前缀,是固定的、
xxx :库的名字,自定义
.a :后缀,固定
Windows: libxxx.lib
制作流程
a、编写需要打包成为静态库的文件,使用命令
g++ -c add.cpp div.cpp sub.cpp mult.cpp
参数c表示只进行汇编,生成.o的文件
b、打包文件生成静态库libcalc.a
ar rcs libcalc.a add.o div.o sub.o mult.o
ar 打包工具
r - 将文件插入备存文件
c - 建立备存文件
s - 索引
静态库的使用
a、将项目的各个文件分文件放好
b、 在当前目录下,使用命令生成可执行文件
静态库的优缺点
优点:静态库被打包到应用程序中加载速度快、发布程序无需提供静态库,移植方便
缺点:消耗系统资源浪费内存、更新部署发布麻烦
动态库
动态库的制作
命名规则:
Linux下 : libxxx.so
lib : 前缀,固定
xxx : 库的名字
.so : 后缀,固定的
在Linux下是一个可执行的文件
windows下 : libxxx.dll
动态库的制作流程:
a、生成.o文件,需要使用参数-fpic ,该参数表示生成与位置无关的目标代码
g++ -c -fpic add.cpp sub.cpp mult.cpp div.cpp
b、生成动态库文件,需要使用参数-shared; calc为动态库名称
g++ -shared add.o sub.o div.o mult.o libcalc.so
动态库的使用
a、将项目的各个文件分文件放好
b、使用动态库和静态库不同 ,静态库会直接把代码打包到可执行程序中,而动态库不会这么做,所以按照静态库使用的方法会无法调用静态库。
可以使用 lld 命令检查动态库的依赖关系
程序如何定位到动态库?
将库文件放入elf格式文件的搜索路径中。
系统加载可执行代码时,需要知道依赖的库的名字的绝对路径。所以动态库需要采用动态载入的方式来获取动态库的绝对路径。对于elf格式(是一种用于可执行文件、目标代码、共享库和核心转储(core dump)的标准文件格式 ,from wikipedia)的可执行程序,是由lb-linux.so来完成文件的搜索,先后搜索顺序为
DT_RPATH段 -- LD_LIBRARY_PATH -- /etc/ld.so.cache文件列表 -- /lib/, /usr/lib
从前往后搜索,找到相应的库文件就会加载到内存中。
具体配置方式:
第一种:添加到环境变量LD_LIBRARY_PATH
a、终端命令行配置(临时,退出终端会失效)
//在终端中输入以下命令配置的环境是临时的,关闭终端会失效 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/atlas/linux/lesson06/library/lib // : 后面为要添加进环境变量的动态库的绝对路径 echo $LD_LIBRARY_PATH //该命令用于查看自行添加的环境变量
b、用户级别的环境配置
cd //回到Home目录下 ll //看到有一个.bashrc文件 vim .bashrc 进入用户配置环境变量的文件 跳转到最后一行(快捷键 shift+G) 另起一行 添加环境变量 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/atlas/linux/lesson06/library/lib 保存退出文件 在终端使用 source .bashrc 或 . .bashrc使环境变量生效
c、系统级别的环境配置
cd //进入家目录 sudo vim /etc/profile //进入系统环境配置文件 添加环境变量到最后一行另起一行,保存文件并退出 source /etc/profile 使环境配置生效
第二种:添加到/etc/ld.so.cache的方式
cd //进入家目录 sudo vim /ld.so.cache //进入文件 直接在后面加上库文件路径 sudo ldconfig //更新文件
第三种: 添加到/lib/, /usr/lib下
不建议,本身是放系统库文件的地方,可能会重名发生错误
动态库优缺点
优点:可以实现进程间资源共享、更新部署发布简单、可以控制何时加载动态库
缺点:加载速度比静态库慢,发布程序时需要提供依赖的动态库
2、Makefile
Makefile文件定义了一系列的规则来指定哪些文件先编译,哪些文件后编译,哪些文件需要重新编译甚至进行更加复杂的工作。
文件命名: makefile 或 Makefile
Makefile规则
·一个Makefile文件可以由一个或多个规则,所有规则都是为第一条规则服务的
目标 ... : 依赖 ...
命令(Shell 命令)
目标: 最终要生成的文件
依赖: 生成目标所需要的文件或目标
命令: 通过执行命令对依赖操作生成目标(命令前必须Tab缩进,8个空格),注意要 g++或者gcc
工作原理:
一些变量:
通配符替换:
文件IO
标准C库的IO函数通过文件指针FILE*对文件进行操作
文件指针是一个结构体,包含了三部分:
文件描述符(整型) 指向了对应的磁盘的文件
文件读写指针位置 读写过程中的指针的实际进行的位置
IO缓冲区(内存地址) 指向对应的内存块
文件写入磁盘的步骤:先由程序写入缓冲区,待缓冲区满了再调用Linux系统的write函数一次性写一部分进磁盘。如果没有缓冲区,文件将每一部分数据写入磁盘时都对硬件进行操作,效率很低。
缓冲区默认大小位8192byte,即8k
虚拟地址空间
创建进程后会产生一个虚拟地址空间,大小由CPU决定。
虚拟地址空间的数据会由MMU映射到真实的物理内存中。
用户区:
环境变量:会加载用户的环境变量,终端下用env可查看
命令行参数:
栈空间:从高地址往低地址存储,存放局部变量
共享库:加载动态库
堆空间:从低地址往高地址存储,存放动态分配数据
ELF:
.bss(存储未被初始化的全局变量,例如: int s;)
.data(存储已初始化的全局变量)
.text(放置代码段,是二进制的机器指令)
受保护地址(存放空指针等数据)
文件描述符
同一终端下,所有打开文件的文件描述符构成一个文件描述符表,该表位于Linux内核的PCB(进程控制块)中。每个终端拥有一个文件描述符表,每个文件描述符表的前三个为标准输入、输出、错误。之后为可以被文件描述符占用的位置,每次打开的文件描述符占用的位置为文件描述符表的空位置中最小的一个。同一文件被打开后未释放前仍可以打开多次,每次的文件描述符都不同。
open打开文件相关:
打开一个文件并操作有两种选择
包含头文件:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//打开一个已有文件
int open(const char *pathname, int flags);
//创建一个新文件
int open(const char *pathname, int flags, mode_t mode);
pathname:文件的路径
flags:文件的操作权限设置
- 必选:O_RDONLY, O_WRONLY, or O_RDWR,三者互斥
- 可选:O_CREAT等 文件不存在则创建新文件,可选权限在必选权限后面用按位或 | 分隔
- flags是一个int类型的数据,占4个字节,32位。每一个位就是一个标志位,按位或可以集合所有需要的权限
mode: 八进制的数,表示创建出的新文件的操作权限,例如:0777
函数返回值是一个文件描述符(file description),打开失败返回-1
errno示例:
errno:是Linux系统函数库里面的一个全局变量,记录的是最近的错误号。
perror(const char* s);
打印errno对应的错误描述,s为用户定义的描述
举例:当前目录下不存在a.txt文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main(){
int fd = open("a.txt", O_RDONLY);
if(fd == -1){
perror("hello");
}
close(fd);
return 0;
}
输出结果;
hello:No such file or directory
open创建文件示例:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
int main(){
int fd = open("create.txt", O_RDWR | O_CREAT, 0777);
/*
最终的权限并不是0777,最终结果是 mode & ~umask的结果。
- umask是系统为更好的设置文件的设置,在终端可通过umask查看
- 可通过umask xxx 来自定义umask
*/
if(fd == -1){
perror("open");
}
close(fd);
return 0;
}
终端中使用ll命令可以查看到文件的操作权限, 三个位为一组。从前到后分别是
当前用户对文件的权限、当前用户所在组对文件的权限、其他组对文件的权限
rwx对应的二进制数为111,即可读可写可执行
r-- 对应的二进制数位100,即可读
read & write函数:
头文件:#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);
参数:
- fd;文件描述符
- *buf: 写入数据的位置,数组的地址
- count: 写入数据的大小
返回值:
- 成功:返回写入的字节数
- 失败: 返回-1,并设置合适的errno
-----------------------------------------------------
read & write 示例:
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(){
//1、打开需要被复制的文件
int srcfd = open("Readme.txt", O_RDONLY);
if(srcfd == -1){
perror("open: ");
return -1;
}
//2、创建复制的目标新文件
int desfd = open("cpy.txt", O_WRONLY | O_CREAT, 0664);
if(desfd == -1){
perror("open: ");
return -1;
}
//3、重复读写
int buf[1024] = {0};
int len = 0; //作为读的返回值
while((len = read(srcfd, buf, sizeof(buf))) > 0){
write(desfd, buf, len); //len是读取到的字节数,读取到多少就写入多少
}
//4、关闭文件
close(srcfd);
close(desfd);
return 0;
}
lseek函数:
头文件:
#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.拓展文件长度
lseek(fd, 100, SEEK_END); 从文件末尾往后偏移100,最后需要写一个数据才能生效
------------------------------------------------------------------------------------------
lseek示例:文件拓展100个字节长度
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(){
int fd = open("hello.txt", O_RDWR);
if(fd == -1){
perror("hello ");
return -1;
}
int ret = lseek(fd, 100, SEEK_END);
if(ret == -1){
perror("lseek");
return -1;
}
write(fd, " ", 1); //需要写入一个数据确保拓展到
close(fd);
return 0;
}
stat & lstat函数:
#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);
作用:获取一个软链接文件相关的一些信息
参数:
- pathname: 操作的文件的路径名
- statbuf: 结构体变量,用于存放文件的信息(传入的是地址)
返回值:
成功:返回0
失败:返回-1,设置errno
----------------------------------------------------------------
stat函数示例:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <iostream>
using namespace std;
int main(){
struct stat statbuf;
int ret = stat("a.txt", &statbuf); //注意传入的是地址
if(ret == -1){
perror("stat");
return -1;
}
cout<<"size: "<<statbuf.st_size<<endl;
return 0;
}
软链接方式:
将b.txt 软链接到 a.txt
ln -s a.txt b.txt
文件类型:
模拟实现ls -l命令(打印文件/文件目录详细信息)
//模拟实现 ls -l
//-rw-rw-r-- 1 atlas atlas 845 Dec 18 01:50 stat.cpp
#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>
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_IFCHR:
perms[0] = '-';
break;
case __S_IFDIR:
perms[0] = 'd';
break;
case __S_IFREG:
perms[0] = '-';
break;
case __S_IFIFO:
perms[0] = 'p';
break;
case __S_IFSOCK:
perms[0] = 's';
break;
case __S_IFBLK:
perms[0] = 'b';
break;
case __S_IFLNK:
perms[0] = 'l';
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", buf);
return 0;
}
文件属性操作函数:
+ access 判断文件是否有某权限
+ chmod 修改文件权限
+ chown 修改文件所有权
+ truncate 缩减或拓展文件大小
access函数:判断文件是否拥有某权限
/*
#include <unistd.h>
int access(const char *pathname, int mode);
作用:判断某个文件是否有某权限,或判断文件是否存在
参数:
-pathname: 文件路径
-mode:
F_OK: 判断文件是否存在
R_OK: 判断文件是否有读权限
W_OK: 判断文件是否有写权限
X_OK: 判断文件是或否有执行权限
返回值:
成功返回0, 失败返回-1
*/
#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;
}
chmod函数:修改文件权限
/*
#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
参数:
-pathname: 文件路径
-mode: 需要修改的权限, 八进制的数
返回值:
成功返回0, 失败返回-1
*/
#include <stdio.h>
#include <sys/stat.h>
int main(){
int ret = chmod("a.txt", 0777); //前面需要加0
if(ret == -1){
perror("chmod");
return -1;
}
printf("权限修改成功!\n");
return 0;
}
chown函数:修改用户组
#include <stdio.h>
#include <unistd.h>
int main(){
int ret = chown("a.txt", 1001, 1001);
if(ret == -1){
perror("chown");
return -1;
}
printf("修改所有者成功!\n");
return 0;
}
truncate函数:文件缩减或拓展至指定大小
/*
#include <unistd.h>
#include <sys/types.h>
int truncate(const char *path, off_t length);
作用:缩减或拓展文件至指定大小
参数:
-path: 文件路径
-length: 文件的目标大小
缩减:将文件原有部分字符从后至前删除
拓展:给文件填充空字符
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main(){
int ret = truncate("a.txt", 100);
if(ret == -1){
perror("truncate");
return -1;
}
printf("文件大小修改成功!\n");
return 0;
}
目录操作函数:
+ getcwd 获取当前进程工作路径
+ chdir 更改工作路径
/*
#include <unistd.h>
int chdir(const char *path);
作用:修改工作路径
参数:需要修改的工作路径
#include <unistd.h>
char *getcwd(char *buf, size_t size);
作用:获得当前工作路径
参数:
-buf: 当前路径的字符串数组
-size: 路径字符串的大小
返回值:文件路径名称
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(){
//获取当前工作路径
char buf[128];
getcwd(buf, sizeof(buf));
printf("当前工作路径为:%s\n", buf);
//修改进程工作路径
int ret = chdir("/home/atlas/webserver/lesson13");
if(ret == -1){
perror("chdir");
return -1;
}
//在新路径下新建一个文件
int fd = open("chdir.txt", O_CREAT | O_RDWR, 0777);
if(fd == -1){
perror("open");
return -1;
}
close(fd);
//获取新的工作路径
char buf1[128];
getcwd(buf1, sizeof(buf1));
printf("当前工作路径为:%s\n", buf1);
return 0;
}
目录遍历函数:
/*
opendir
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
作用: 打开文件目录,
参数: 文件目录名称
返回值: 成功返回文件流数据结构, 失败返回NULL
readdir
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
作用: 读取目录文件中的数据
参数: 是opendir中的返回值
返回值: 代表读取到的文件的信息,
struct dirent {
ino_t d_ino; Inode number
off_t d_off; Not an offset; see below
unsigned short d_reclen; Length of this record
unsigned char d_type; Type of file; not supported
by all filesystem types
char d_name[256]; Null-terminated filename
};
读到失败或末尾返回NULL
closedir
#include <sys/types.h>
#include <dirent.h>
int closedir(DIR *dirp);
作用:关闭文件目录
参数:DIR* 类型
返回值: 成功返回0, 失败返回-1
*/
//读取目录内文件
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int readFileNum(const char *pathname);
int main(int argc, char* argv[]){
/*
argc 是传入的参数个数
argv[0] 是执行文件的名称: ./XXX, 若只执行文件不传入参数则argc = 1
argv[1] 是执行文件时传入的第一个参数: ./XXX xxx ,此时argc = 2
*/
//小于2说明没有传入想要查询的目录
if(argc < 2){
printf("%s path\n", argv[0]);
return -1;
}
int num = readFileNum(argv[1]);
printf("普通文件个数为:%d\n", num);
return 0;
}
//获取目录下所有文件个数
int readFileNum(const char *pathname){
DIR* dir = opendir(pathname); //打开文件目录
if(dir == NULL){
perror("opendir");
exit(0);
}
int total = 0;
struct dirent* dirptr;
//结束条件为到目录末端
while((dirptr = readdir(dir)) != NULL){
char *dname = dirptr->d_name; //获取文件名称
/*
int strcmp(const char *s1, const char *s2);
参数:两个字符串
返回值:相等返回0 s1 > s2 返回 正数, s1 < s2 返回负数, 比较的是从前往后的ASCII码值
*/
//当前目录. 和 上一级目录.. 不用计算
if(strcmp(dname, ".") == 0 || strcmp(dname, "..") == 0){
continue;
}
//目录需要递归计算
if(dirptr->d_type == DT_DIR){
char newpath[256];
sprintf(newpath, "%s/%s", pathname, dname);
total += readFileNum(newpath);
}
//文件
if(dirptr->d_type == DT_REG){
total++;
}
}
closedir(dir); //关闭文件目录
return total;
}
dup函数:复制新文件描述符
/*
#include <unistd.h>
int dup(int oldfd);
作用:复制一个新的文件描述符
新的文件描述符和旧文件描述符指向的是同一个文件
新的文件描述符是空间的未被使用的文件描述符的第一个
*/
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(){
int fd = open("a.txt", O_RDWR | O_CREAT, 0664);
if(fd == -1){
perror("open");
return -1;
}
int fdNew = dup(fd);
if(fdNew == -1){
perror("dup");
return -1;
}
printf("fd: %d, fdNew: %d\n", fd, fdNew);
close(fd);
char *buf = "Hello, world!";
int ret = write(fdNew, buf, strlen(buf));
if(ret == -1){
perror("write");
return -1;
}
close(fdNew);
return 0;
}
dup2函数:重定向文件函数
/*
#include <unistd.h>
int dup2(int oldfd, int newfd);
作用:重定向文件
用新的文件描述符newfd指向oldfd代表的文件,newfd会关闭原文件,让newfd指向oldfd指向文件
*/
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(){
int fd = open("1.txt", O_RDWR | O_CREAT, 0664);
if(fd == -1){
perror("open");
return -1;
}
int fd1 = open("2.txt", O_RDWR | O_CREAT, 0664);
if(fd == -1){
perror("open");
return -1;
}
printf("fd: %d, fd1: %d\n", fd, fd1);
int fd2 = dup2(fd, fd1);
if(fd2 == -1){
perror("dup2");
return -1;
}
printf("fd: %d, fd1: %d, fd2: %d\n", fd, fd1, fd2);
char *buf = "hello dup2";
int ret = write(fd1, buf, strlen(buf));
if(ret == -1){
perror("write");
return -1;
}
return 0;
}
fcntl函数:文件控制函数
/*
#include <unistd.h>
#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg );
参数:
-fd: 需要控制的文件描述符
-cmd: 表示对文件描述符进行哪些操作
F_DUPFD: 复制文件文件描述符,复制第一个参数fd,得到新的文件描述符
用法: int res = fcntl(fd, F_DUPFD);
F_GETFL: 获取文件描述符的状态flags,获取到的flags和open是赋予的一致
F_SETFL: 设置文件描述符的状态,必选项不能设置,只能设置可选项
必选项: O_RDONLY, O_WRONLY, O_RDWR
可选项: O_APPEND, O_NONBLOCK ...
O_APPEND 是以给文件追加内容
O_NONBLOCK 是文件设置为非阻塞式
一般设置的时候需要先用F_GETFL获取已有的状态,然后再加上需要设置的,不然会覆盖之前的可选项
*/
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
int main(){
int fd = open("1.txt", O_RDWR);
char *buf = "hello";
int ret = write(fd, buf, sizeof buf);
if(ret == -1){
perror("write");
return -1;
}
//1.复制文件描述符
//int ret = fcntl(fd, F_DUPFD);
//先获取文件状态
int flags = fcntl(fd, F_GETFD);
if(flags == -1){
perror("fcntl");
return -1;
}
flags |= O_APPEND; //表示加上追加写入的权限
//修改文件权限
ret = fcntl(fd, F_SETFL, flags);
if(ret == -1){
perror("fcntl");
return -1;
}
//追加写入
char *buffer = " world!";
ret = write(fd, buffer, strlen(buffer));
if(ret == -1){
perror("write");
return -1;
}
close(fd);
return 0;
}