文件相关系统调用接口
#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)
int creat(const char *pathname, mode_t mode);
pathname:要打开或创建的目标文件
flags:打开文件时,传入的多个参数选项,可用一下多个常量进行“或”运算构成
flags参数:
O_RDONLY:只读,默认值为此
O_WRONLY :只写
O_RDWR:可读可写
以上三个为必选选项,并且只能选择一个
O_CREAT:若文件不存在,则创建它,此时需要使用mode选项,来指明新文件的访问权限;若文件存在则打开
O_EXCL:一般与O_CREAT一起使用,若文件存在则报错
O_APPEND:写入数据使用追加写方式
O_TRUNC:打开文件时清空原有数据
mode_t mode:不定参
返回值:错误返回-1;成功返回文件描述符,即打开文件的操作句柄。
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
fd:文件描述符,open返回的
buf:指定的缓冲区,数据的放置位置
count:要读取文件的数据长度字节数
返回值:成功时返回读取到的字节数(为0表示读到文件末尾),当返回值小于指定的字节数有可能已经接近文件结尾,或者正在从管道或者终端读取数据,或者read()被信号中断,失败时返回-1。
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
fd:文件描述符,open返回的
buf:指定的缓冲区,要写入数据的缓冲区的首地址
count:要写入文件的数据长度字节数
返回值:成功返回实际写入的字节数,失败时返回-1。
#include <unistd.h>
int close(int fd);
fd:文件标识符
返回值:成功返回0,失败返回-1。
文件描述符fd与FILE结构体对比
文件描述符fd:文件描述符是一个整数,没有负数在open时产生。在Linux系统下,文件描述符主要被用来标识一个文件。打开文件时,操作系统在内存中创建相应的数据结构来描述目标文件。FILE结构体即为这样的数据结构,表示一个已经打开的文件对象。进程执行open系统调用时,需要关联进程和文件,所以每个进程都有一个file指针,指向file_struct文件表,其中包含一个指针数组,每个元素都是一个指向打开文件的指针。本质上,文件描述符就是该数组下标,通过文件描述符,寻找对应的文件。fd起到一个索引的作用,进程通过PCB中的文件描述符表找到该fd所指向的文件指针,内核通过文件对象表来管理系统中各种各样的文件,文件表通过指针指向打开的文件,从而管理整个文件系统。
FILE结构体(文件流指针 FILE *fp):IO相关函数与系统调用接口对应,库函数封装系统调用,访问文件通过fd访问。C标准库当中的FILE结构体内部封装fd。所以FILE比fd更适合跨平台。C中使用文件指针来做为I/O的句柄,文件指针即指向进程的用户空间中的一个FILE结构体,内部有文件描述,有缓冲区。
文件描述符(fd)是唯一的,文件指针(FILE,lib层,即C库)不是唯一的,但指向的对象是唯一的。
shell增加重定向
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#include <ctype.h>
#include <fcntl.h>
#define N 1024
#define MIX 32
int main()
{
while (1)
{
// 输入提示
printf("[mini@localhost]# ");
// 清空缓冲区
fflush(stdout);
char buf[N] = {0};
// 获取输入的字符串,一直接收至遇到换行符
fgets(buf, N - 1, stdin);
buf[strlen(buf) - 1] = '\0';
printf("cmd: [%s]\n", buf); // 查看数据接收是否正确
// 解析重定向符号> >>
int flag = 0;
char *p = buf, *file = NULL;
while (*p != '\0')
{
if (*p == '>')
{
flag = 1;
*p = '\0';
p++;
if (*p == '>')
{
flag = 2;
p++;
}
while (*p == ' ')
p++;
file = p;
while (!isspace(*p) && *p != '\0')
p++;
*p = '\0';
}
p++;
}
// 解析输入字符串,获取需要运行的程序名称及参数
int argc = 0;
char *argv[MIX] = {NULL}; // 指针数组
char *ptr = buf;
while (*ptr != '\0')
{
if (!isspace(*ptr))
{
argv[argc] = ptr;
argc++;
while (!isspace(*ptr) && *ptr != '\0')
ptr++;
*ptr = '\0';
}
ptr++;
}
argv[argc] = NULL;
int i = 0;
for (i = 0; i < argc; i++)
printf("[%s]\n", argv[i]); // 查看数据解析是否分段正确
// 这里输出加\n为了刷新缓冲区内容,如果用空格则会在程序本次循环退出时才输出这段内容
// 程序替换,调用子进程
int pid = fork();
if (pid == 0) // 子进程
{
if (argv[0] == "cd") // 单独处理cd指令
chdir(argv[1]); // chdir 是C语言中的一个系统调用函数(同cd)
// 单独处理重定向符号
if (flag == 1)
{
int fd = open(file, O_RDWR | O_CREAT | O_TRUNC, 0664);
dup2(fd, 1); // 重定向一个标准输出到fd中
}
else if (flag == 2)
{
int fd = open(file, O_RDWR | O_CREAT | O_APPEND, 0664);
dup2(fd, 1);
}
execvp(argv[0], argv);
exit(0);
}
waitpid(-1, NULL, 0); // 父进程等待子进程结束
}
return 0;
}
// 经测试运行程序在输入指令的时候,backspace键按下会显示^H,无法删除已输入的字符
// 按下delete键会显示^[[3~,同样无法删除已输入的字符
// 键盘上下左右键按下会显示^[[A^[[B^[[D^[[C
// ls >> test 运行成功将内容写入test文件中,但是输入字符串则失败
静态库与动态库
静态库(lib库名.a):windows下后缀(.lid),程序在编译链接的时候把库的代码链接到可执行文件里,程序运行的时候将不需要静态库。
静态库的生成(gcc、ar):
-c 创建静态库;-r 重新替换改动的.o文件
gcc -c name.c -o name.o
ar -cr libname.a name.o
动态库(lib库名.so):windows下后缀(.dll),程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
动态库的生成(使用gcc):
-fPIC 产生位置无关代码;–share 生成动态/共享库
gcc -fPIC -c name.c -o name.o
gcc --share name.o libname.so
库的使用:
连接的时候使用库:
- 将库文件放置到指定的路径下/lib64、/usr/lib64
- 设置LIBRARY_PATH环境变量
- 通过gcc -L选项设置链接库的默认检索路径,指定库文件所在路径
运行的时候使用库:
只有动态库才会在运行的时候加载库。
- 将库文件放置到指定的路径下/lib64、/usr/lib64
- 设置LD_LIBRARY_PATH环境变量
动/静态库的练习与使用
add.c
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
sub.c
#include <stdio.h>
int sub(int a, int b)
{
return a - b;
}
mul.c
#include <stdio.h>
int mul(int a, int b)
{
return a * b;
}
div.c
#include <stdio.h>
int div(int a, int b)
{
if (b == 0)
return -1;
else
return a / b;
}
main.c
#include <stdio.h>
int main()
{
int a = 8, b = 2;
printf("add(a,b) = %d\n", add(a, b));
printf("sub(a,b) = %d\n", sub(a, b));
printf("mul(a,b) = %d\n", mul(a, b));
printf("div(a,b) = %d\n", div(a, b));
return 0;
}
生成静态库:
使用静态库:
删除静态库后已经编译好的程序仍然可以运行:
生成动态库:
动态库的使用: