文章目录
- 文件操作
-
- [文件IO API](#文件IO API)
- 目录操作
- 消息摘要算法
-
- [openssl/sha API](#openssl/sha API)
- [openssl/evp API](#openssl/evp API)
- 进程与线程
- 线程同步
- 网络编程
-
- 网络核心概念
- [TCP/IP 协议](#TCP/IP 协议)
-
- [7层IOS-OSI 开放系统互联参考模型](#7层IOS-OSI 开放系统互联参考模型)
- 4层TCP/IP
- TCP建立连接和断开连接
- 套接字编程(传输层)
- 应用层协议编程(应用层)
- nginx
- 信号
- 服务相关
文件操作
linux / unix 一切皆是文件:文件、文件夹、设备、进程
文件:文件描述符
文件分类
- 文本文件:可直接阅读和编辑
- 二进制文件:目标文件、可执行文件、图片...
文本文件有行的概念,一行的结尾有换行,有文件结束标志
文件IO API
结构体:
- FILE 文件 stdio.h
函数:
-
标准库 stdio
- fopen(const char *path, const char *mode)
访问模式 mode: w r a r+ w+ a+ rb wb ab 若是二进制文件,mode以b结尾
- fclose(FILE *fp)
- fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
- fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
- fseek(FILE *stream, long offset, int whence)
- ftell(FILE *stream)
- rewind(FILE *stream)
- fflush(FILE *)
-
Linux 底层接口函数 sys/types.h sys/stat.h fcntl.h unistd.h
- open(const char *pathname, int flags, mode_t mode)
- close(int fd)
- read(int fd, void *buf, size_t count)
- write(int fd, const void *buf, size_t count)
- lseek(int fd, long int offset, int whence)
目录操作
dirent.h
sys/types.h
unistd.h
结构体:
DIR
dirent // 目录信息
函数:
- opendir(char*)
- closedir(DIR*)
- readdir(DIR*) 返回静态分配的dirent* 文件夹信息
- chdir(char*) 切换目录
- mkdir(char*, mode_t) 以mode | ~umask | 0777的模式创建目录
- remove(char*)
- rename(char *old, char *new)
c
chdir("test");
DIR *dir = opendir(".");
if (NULL == dir)
{
fprintf(stderr, "目录不存在,或者无法打开!\n");
return -1;
}
struct dirent *d;
printf("inode\t类型\t名称\n");
while(d = readdir(dir))
{
printf("%lu\t%d\t%s\n", d->d_ino, d->d_type, d->d_name);
}
closedir(dir);
mkdir("abc", 0700);
remove("abc");
mkdir("abc", 0700);
rename("abc", "test");
消息摘要算法
一种单向,不可逆算法,任意长度的输入,生成固定的输出;非压缩、非加密,而是摘要信息,做数据校验
- md5 不可靠
- sha128 不可靠
- sha256 *
- sha512
- ...
应用:
- 分布式计算模型
- 计算机密码
- 货币
- 区块链技术
openssl/sha API
安装:
apt install openssl
apt insatll libssl-dev
结构体:
SHAn_CTX ,sha上下文,n 为 256、512...
函数:
- SHA256_Init(&ctx); 初始化
- SHA256_Update(&ctx, data, sizeof(data)); 输入摘要信息,可以多次输入
- SHA256_Final(md, &ctx); 获得摘要信息
c
// 初始化
SHA256_CTX ctx;
SHA256_Init(&ctx);
// 写入摘要信息
char data[] = "hello";
SHA256_Update(&ctx, data, sizeof(data));
// 获得摘要信息
unsigned char md[SHA256_DIGEST_LENGTH];
SHA256_Final(md, &ctx);
// 转成16进制数
for (size_t i = 0; i < SHA256_DIGEST_LENGTH; ++i)
{
printf("%0x", md[i]);
}
printf("\n");
编译:
cc sha.c -o sha -L/usr/lib/openssl -lssl -lcrypto
openssl/evp API
结构体:
EVP_MD_CTX 上下文
EVP_MD 算法函数类型
函数:
- EVP_MD_CTX_create() 创建上下文
- EVP_DigestInit(ctx, mdalg) 初始化摘要指定生成的算法
- EVP_DigestUpdate(ctx, "hello", sizeof("hello")) 写入新内容到摘要
- EVP_DigestFinal(ctx, md, 1) 生成摘要
- EVP_Digest("hello", 5, r, NULL, EVP_sha256(), NULL) 直接以算法生成摘要
进程与线程
程序
- 为了完成任务编写的多个指令和数据的集合,以文件的形式存在磁盘中
进程
- 程序的一次执行过程(实例),及该过程中获得的资源:内存、cpu时间
- 一个程序可能有多个进程
- 系统资源的分配单位
/proc:虚拟文件系统,包含进程的相关信息
/proc/sys/kernel/pid_max:进程号最大值
线程
- 进程中的执行单元
协程
- 轻量级线程
- 由开发者调度
进程状态
- 创建
- 就绪 等待CPU时间片
- 运行 正在使用CPU执行
- 阻塞 等待某种事件发生而暂时不能运行,让出cpu时间片
- 挂起 进程被暂停,不能被调度执行,处于内存之外
- 终止
进程创建与操作
unistd.h
- system(cmd) 使用shell命令创建,命令后加& 后台脱离父进程执行
- fork 复制当前进程的虚拟地址映像,创建子进程
- execl(path, ..., NULL) 执行指定程序,替代当前虚拟地址映像
- sleep() 挂起线程
- wait(&status) 等待子进程状态改变(终止,信号终止,信号恢复执行)
线程
pthread.h
- pthread_create(pthread_t, attr_t*, void (func)(void ), void * arg) void (func)(void)为返回void的参数为void的函数,为避免参数在离开创建线程的作用域中遭到修改或销毁,应用静态变量或者堆变量,对于func的返回值也是一样的道理
- pthread_exit(void *retval);
- pthread_join(pthread_t, void ** ret); 等待线程结束
其它库:openmp
任务类型:IO密集,cpu密集
对于cpu密集型的应用程序,线程数一般设置为 CPU 核心数或者稍微多一点(例如 CPU 核心数 + 1 或 + 2),这样可以充分利用 CPU 资源,避免过多的上下文切换带来的开销。
对于 I/O 密集型的应用程序,线程数可以设置得更高一些,比如 2(2n+1个)~4 倍于 CPU 核心数,这样可以更好地隐藏 I/O 操作的延迟。
时间
time.h
time_t 日历时间
struct tm 分解时间
- time() 返回秒
- mktime™ 返回time_t 本地
- gmtime(time_t) 返回静态分配的tm UTC
- localtime(time_t) 返回静态分配的tm 本地
- asctime™ 返回静态分配的字符串时间 UTC
- ctime(time_t) 返回静态分配的字符串时间 本地
- strftime(str,format,tm) 格式化时间字符串 本地
- strptime(str,format,tm) 从字符串得到时间 本地
sys/time.h
timeval - gettimeofday(tv, NULL) 返回微秒级时间
线程同步
多个进程或者多个线程访问公共的资源时,可能出现的同时修改资源,引发共享资源值的不一致或出现异常,可以通过同步避免
同步方法
- 信号量
- 互斥锁
- 条件变量
...
信号量 sem
semaphore.h
sem_t
- sem_init(&sem_t, pshare, value)
pshare为0表示在线程间通信,为非0在进程间通信, value为初始值
- sem_wait()
如果信号量为0,则阻塞,否则将信号量减1
- sem_post()
将信号量加1,如果信号量大于0,则通知因sem_wait()阻塞的进程
- sem_destory()
摧毁信号量
互斥锁
pthread.h、unistd.h
pthread_mutex_t mtx = PTHREAD_MATUEX_INITIALIZER 静态初始化
- pthread_mutex_lock 上锁
- pthread_mutex_unlock 解锁
其它相关锁
- 读写锁 rwlock
- 自旋锁 spinlock 遇到锁定的自旋锁后,不进入阻塞,延时一段时间再看是否解锁(busywait),需要借助标准库中原子操作完成(stdatomic.h)或者openmp(多线程编程库)
条件变量
pthread.h、unistd.h
pthread_cond_t con = PHTREAD_CON_INITIALIZER 静态初始化
- pthread_cond_init() 动态初始化
- pthread_cond_wait(con*, mtx*) 阻塞并释放锁
- pthread_cond_timewait(con*, mtx*, t)
- pthread_cond_signal(con*) 通知至少一个因wait阻塞的线程
- pthread_cond_broadcast(con*) 通知所有线程
- pthread_cond_destory()
生产者消费者模型
c
/*
* 生产者消费者模型
*/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#define MAX_SIZE 5
int num = 0;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
void* customer(void*)
{
pthread_mutex_lock(&mtx);
while(1)
{
if (num <= 0)
{
printf("空了!消费者阻塞\n");
pthread_cond_wait(&cond, &mtx);
}
printf("消费者消费了%d\n", num);
--num;
pthread_cond_signal(&cond);
}
pthread_mutex_unlock(&mtx);
}
void* productor(void* arg)
{
// 获得参数
int n = *(int*)arg;
// 生产
pthread_mutex_lock(&mtx);
for (int i = 0; i < n; i++)
{
if (num >= MAX_SIZE)
{
printf("满了!\n");
pthread_cond_wait(&cond, &mtx);
}
++num;
printf("生产了:%d, 序号:%d\n", num, i);
pthread_cond_signal(&cond);
}
pthread_mutex_unlock(&mtx);
}
int main(int argc, char const *argv[])
{
pthread_t p1, p2;
int n = 53;
pthread_create(&p1, NULL, productor, &n);
pthread_create(&p2, NULL, customer, NULL);
pthread_join(p1, NULL);
pthread_join(p2, NULL);
printf("end.....\n");
return 0;
}
网络编程
网络核心概念
网络:通过有线或者无线的介质连接多个网络设备进行通信
有线:双绞线、同轴电缆、光钎
无线:WiFi、移动网络、蓝牙、红外、NFC
设备:计算机、交换机、路由器、IOT
设备如何标识?
- ip:逻辑地址
- IPv4 32位
- IPv6 128位
- Mac 地址:物理地址、设备标识,不可变
- 主机名
- 域
DHCP:动态地址分配协议
DNS:域名解析服务
- .com
- .org
- .net
- .edu
子网掩码:如255.255.255.0,用以获得网段
port:端口号,65536个,标识了进程中的子任务
1 ~ 1824 保留
网络拓扑结构:星型(局域网,中央节点)、总线型(机房)、环形、混合
浏览器输入地址,在页面出现前都发生了什么事
TCP/IP 协议
网络协议:为了保证通信而定义的规则或约定
7层IOS-OSI 开放系统互联参考模型
- 应用层
- 表示层
- 会话层
- 传输层
- 网络层
- 数据链路层
- 物理层
4层TCP/IP
设计者:文森·瑟夫
实现者:比尔乔伊 BSD SUM公司创始人
- 应用层:http、ssh、scp、ftp、pop3、VoIP (应用程序自定义)
- 传输层:TCP(字节流)、UDP(数据报)
- 网络层 ip、ICMP
- 数据链路层:电气接口,设备驱动,以太网协议
TCP | UDP | |
---|---|---|
连接 | 建立 | 非建立 |
有序 | 是 | 不确保 |
完整 | 丢包会重传 | 不确保 |
数据格式 | 字节流 | 数据报 |
场景 | HTTP、FTP... | 视频、音频... |
TCP建立连接和断开连接
三次握手建立,四次挥手断开
套接字编程(传输层)
概念:
- 通信端点:套接字是一对网络上进程间通信的端点。每个套接字都由一个IP地址和一个端口号唯一标识。
- 抽象概念:它是网络通信的一种抽象模型,用来描述两个进程之间的通信方式。
- 编程接口:在程序设计中,套接字提供了一组API,用于创建连接、发送数据、接收数据以及关闭连接等操作。
- 设备:文件描述符、管道设备。
用到的头文件:
- sys/types.h
- sys/un.h
- netinet/in.h
- arpa/inet.h
- sys/socket.h
使用到的结构体
基本通信地址
c
struct sockaddr
{
unsigned short sa_family; //地址协议簇
char sa_data[14]; //地址封装: 端口号2byte,IP地址4byte
}
本地路径名地址
sys/un.h
c
struct sockaddr_un
{
__SOCKADDR_COMMON (sun_);
char sun_path[108]; /* Path name. */
};
绑定成功后会产生一个名字是Path name的符号链接
网络通信地址
netinet/in.h
c
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr; //INADDR_ANY 任意地址
};
// 网络套接字转换成基本套接字:直接强转
struct sockaddr_in
{
sa_family_t sin_family; //对应基本地址中的sa_family
in_port_t sin_port; /* 端口号 */
struct in_addr sin_addr; /* IP地址 */
/*填充字节,使用的时候直接清零. */
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
};
sin_port和sin_addr都要是相应的网络字节序
字节序
- 大端序 网络字节序 数据高位存储在内存的低地址
- 小端序 主机字节序(x86默认) 数据低位存储在内存的低地址
点分二进制ip转网络字节序字节表示:
- int inet_addr(char*)
- int inet_pton(int af, char* src, void*)
主机字节序与网络字节序转换:
- uint32_t htonl(uint32_t hostlong)
- uint16_t htons(uint16_t hostshort)
- uint32_t ntohl(uint32_t netlong)
- uint16_t ntohs(uint16_t netshort)
socket API
- socket(domian, type, proto) domain 协议族、type(SOCK_STREAM、SOCK_DGRAM)返回文件描述符
- bind(fd, sockaddr*, socklen)
- listen(fd, max_conn)
- accept(fd, sockaddr*, socklen) 返回对端文件描述符
- read()
- write()
- send(fd, buf, size, flag)
- recv(fd, buf, size, flag)
- sendfile(outfd, infd, offset, size) sys/sendfile 直接从内核高速缓存区传入套接字缓冲区 零拷贝
send/recv flag:
recv:
MSG_DONTWAIT 非阻塞方式
MSG_OOB 有带外数据
MSG_WAITALL 强制读len长度才发送
MSG_PEEK 读数据但是不将缓冲区中数据取出
send:
MSG_DONTWAIT 非阻塞方式
MSG_OOB 有带外数据
MSG_MORE 写的时候直到不再指定该标志时才发送该数据(数据打包)
MSG_NOSIGNAL 写的时候挂起信号,进程不被信号中断
应用层协议编程(应用层)
nginx
nginx是一个web服务器,接收请求,发送html
服务器的默认目录 /usr/share/nginx/html
信号
SIGABRT终止进程并产生核心转储文件
SIGCLHD 子进程状态改变
SIGPIPE 套接字读端关闭,写端写入数据时会发出该信号
SIGSEGV 引用到虚拟映像中无效内存(访问栈区与堆区中间未分配的地址、内核区、文本段前面的区域,修改只读区)
SIGINT 终端输入中断命令Ctrl + C中断进程
SIGTERM 发送该信号杀死进程(kill命令默认发送的信号)
SIGKILL 杀死进程(kill -9命令必杀)
SIGSTOP 必停信号
SIGCONT恢复已停止的信号
SIGKILL、SIGSTOP 不能被阻塞、捕获
signal(sig, handler_t) 为信号安装信号处理例程,默认为SIG_IGN(忽略信号)或SIG_DFL(终止进程)
sigpromask(sig, &sigmask, &oldsigmask)设置信号掩码阻塞信号
服务相关
不挂起进程并将输出输入信息写入log.txt
nohup cmd & > log.txt