系统编程概述
操作系统的职责:
操作系统用来管理所有的资源,并将不同设备和不同程序关联起来
Linux 系统编程:
在Linux操作系统的环境下编程, 并使用该操作系统提供的系统调用及各种库, 对系统资源进行访问
系统调用概述
类 UNIX 系统的软件层次:
系统调用是 操作系统提供给用户程序的一组“ 特殊” 函数接口
用户程序可通过 该组接口获得 操作系统( 内核) 提供的服务
例子:
用户可通过文件系统相关的系统调用, 请求系统打开文件、 关闭文件或读写文件
系统调用按功能逻辑:
进程控制、 进程间通信、文件系统控制、系统控制、内存管理、网络管理、socket 控制、用户管理
系统调用的返回值:
成功: 0
失败:负值
错误信息存放在全局变量 errno,可用 perror 函数打印出错信息
系统调用遵循的规范
在 Linux 中, 应用程序编程接口(API)遵循 POSIX 标准
POSIX 标准基于当时现有的 UNIX 实践和经验, 描述了 操作系统的系统调用编程接口(实际上就是 API), 用
于保证应用程序可以在源代码一级上在多种操作系统上移植运行
如:
linux 下写的 open、 write 、 read 可以直接移植到 unix 操作系统下
系统调用 I/O 函数
系统调用中操作 I/O 的函数, 都是针对文件描述符
通过文件描述符可以直接对应的文件进行操作
如:
open、 close、 write 、 read、 ioctl
文件描述符
文件描述符是非负整数
打开现存文件或新建文件时,系统(内核) 会返回一个文件描述符
文件描述符用来指定 已打开的文件
// 程序运行后这三个文件描述符是默认打开
#define STDIN_FILENO 0 //标准输入的文件描述符
#define STDOUT_FILENO 1 //标准输出的文件描述符
#define STDERR_FILENO 2 //标准错误的文件描述符
open 函数
打开一个文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
// 文件存在
int open(const char *pathname, int flags);
/* 文件不存在
* pathname: 文件的路径及文件名
* flags:行为标志
* O_RDONLY 只读方式打开
* O_WRONLY 只写方式打开
* O_RDWR 可读可写方式打开
* 下列值 位或
* O_CREAT 文件不存在则创建文件,mode说明文件权限
* O_EXCL 指定O_CREAT,且文件已存在 则出错
* O_TRUNC 文件存在 清除文件内容
* O_APPEND 写文件,数据添加到文件末尾
* O_NONBLOCK 打开的文件是FIFO,字符文件,块文件,该选项为非堵塞标志位
* mode:文件权限(可读,可写,可执行)的设置
* 取值 八进制 含义
* S_IRWXU 700 文件所有者的读,写,可执行权限
* S_IRUSR 400 文件所有者的读权限
* S_IWUSR 200 文件所有者的写权限
* S_IXUSR 100 文件所有者的可执行权限
* S_IRWXG 70 文件所有者同组用户的读写可执行权限
* S_IRGRP 40 文件所有者同组用户的读权限
*
* return:成功:文件描述符 失败:-1,可用 perror 查看原因
*/
int open(const char *pathname, int flags, mode_t mode);
close 函数
关闭一个文件
#include <unistd.h>
/*
*fd:调用open打开文件返回的文件描述符
* return:成功:0 失败:-1 可用 perror 去查看原因
*/
int close(int fd);
write 函数
指定数目的数据写到文件中
#include <unistd.h>
/*
* fd:文件描述符
* addr:数据首地址
* count: 写入数据的字节个数
* return:成功:写入字节个数 失败:-1
*/
ssize_t write(int fd, const void *addr, size_t count);
read 函数
指定数目的数据读到内存
#include <unistd.h>
/*
* fd:文件描述符
* addr:内存首地址
* count:读取的字节个数
* return:成功:读取的字节个数 失败:-1
*/
ssize_t read(int fd, void *addr, size_t count);
remove 库函数
删除文件
#include <stdio.h>
/*
* pathname: 文件路径+文件名
* return:成功:0 失败:-1
*/
int remove(const char * pathname);
例子:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
void test1();
void test2();
void test3();
int main(int argc, char *argv[])
{
test1();
test2();
test3();
return 0;
}
void test1()
{
char buf[32] = "";
read(0, buf, sizeof(buf));
write(1, buf, strlen(buf));
}
void test2()
{
int fd = 0;
char str[] = "hello file";
fd = open("./a.txt",O_WRONLY | O_CREAT | O_EXCL, 0777);
if(-1 == fd)
{
perror("open");
return;
}
printf("fd = %d\n", fd);
write(fd, str, strlen(str));
close(fd);
}
void test3()
{
int fd = 0;
char buf[128] = "";
int len = 0;
fd = open("a.txt",O_RDONLY);
if(-1 == fd)
{
perror("open");
return;
}
len = read(fd, buf, sizeof(buf));
printf("len = %d\n", len);
printf("buf = %s\n", buf);
close(fd);
}
打印:
系统调用与库函数
不需要调用系统调用
不需切换到内核空间 即可完成函数全部功能, 并且将结果反馈给应用程序
如 :strcpy、 bzero 等字符串操作函数
需要调用系统调用
需要切换到内核空间, 这类函数通过封装系统调用去实现相应功能
如 printf、 fread 等
库函数与系统调用的关系
并不是所有的系统调用都被封装成了库函数, 系统提供的很多功能都必须通过系统调用才能实现
系统调用是需要时间, 程序中 频繁使用 系统调用会 降低程序的运行效率
当运行内核代码时, CPU 工作在内核态, 在系统调用发生前需要保存用户态的栈和内存环境, 然后转入内核
态工作
系统调用结束后,又要换回用户态切。 这种环境的切换会消耗掉许多时间
库函数访问文件的时候根据需要,设置不同类型的缓冲区, 从而减少了直接调用 IO 系统调用的次数, 提高了访问
效率
应用程序调用 printf 函数时, 函数执行的过程
实现 cp 命令
目标:
使用系统调用实现 cp 命令
原理:
用系统调用 open 打开文件, 使用 read 从文件读数据, 使用 write 向文件写数据
传给可执行程序的参数个数存放在 main 函数的 argc 中, 参数首地址存放在指针数组 argv 中
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const * argv[])
{
int srcFd = 0;
char fileName[64] = "";
int dstFd = 0;
char buf[128] = "";
int len = 0;
if(3 != argc)
{
printf("请按 ./a.out srcdst 传参\n");
return 0;
}
// 打开源文件
srcFd = open(argv[1], O_RDONLY);
if(-1 == srcFd)
{
perror("open");
return 0;
}
// 打开目的文件
sprintf(fileName, "%s/%s",argv[2],argv[1]);
dstFd = open(fileName, O_WRONLY | O_CREAT, 0777);
if(-1 == dstFd)
{
perror("open");
return 0;
}
while(1)
{
len = read(srcFd, buf, sizeof(buf));
if(len <= 0)
{
break;
}
write(dstFd, buf, len);
}
close(srcFd);
close(dstFd);
return 0;
}