linux 系统编程

文章目录

文件操作

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

相关推荐
知名“矿工”3 分钟前
MacroSan 2500_24A配置
linux
c++初学者ABC3 分钟前
【基础】卒的遍历(DFS)
c++·算法
阳区欠3 分钟前
【Linux】Linux的基础工具
linux·服务器·git·vim·gcc/g++·makefile/make·gdb/cgdb
我是唐青枫5 分钟前
Linux axel 下载加速命令详解
linux·运维·服务器
五阿哥爱跳舞20 分钟前
阿里云新用户服务器配置
运维·服务器
背着黄油面包的猫23 分钟前
计算机网络-物理层
网络·计算机网络
Thmos_vader28 分钟前
【minicom】Linux串口调试工具 - minicom的安装及使用
linux·测试工具·minicom·串口调试工具
C囧囧38 分钟前
Ubuntu 22.04.5 修改IP
linux·ip配置·netplan
MUTA️1 小时前
AutoDL服务器深度学习使用过程
服务器·人工智能·深度学习·计算机视觉
m0_748234711 小时前
Jo-im开发:用于WebRTC的ICE中继服务器Coturn搭建
运维·服务器·webrtc