二阶段总结

一、linux系统编程简介

1)好处:1)可以让我们的程序跑的更快,2)可以让我们的程序和远端的另一个程序进行通信或数据交换

2)包含内容:

文件IO:可以让我们程序中的数据可以持久化的保持在我们的电脑上

进程与线程:可以让我们的程序实现并发(跑的更快)

网络编程:可以实现网络中的两个设备能够进行通信

3)如何学

这个阶段的学习和C语言不一样,它不在讲语法,主要学的是应用,内容相对比较抽象

如何学好它:

1.知道学的这个知识点时做什么的,有什么特点:文件可以持久化的保持程序运行中数据

2.使用这个知识点 它的基本的使用流程:打开文件,读写文件,关闭文件

3.学会使用在知识点的流程中涉及的函数,我们在看到它后 能够使用它。


二、文件IO

1)什么是文件

文件就是存储在磁盘上的数据集合。

在linux中,一切都是文件,1)可以实现数据的保存 2) 文件是连接用户和设备之间的一个桥梁 /dev

2)如何来标识一个文件

从用户的角度,通过文件名来标识计算机中的一个文件,文件名:=文件路径+文件主名+扩展名

D:\2511\DAC_TEST\Drivers\STM32F4xx_HAL_Driver\Inc\aa.txt

文件名是给看用户看的,用来标识一个静态文件。

文件描述符是给进程看的,使用标识在一个进程中被打开的文件。

3)文件分类

普通文件-:记事本,源码文件,视频,音频文件,pdf,word,压缩包等

目录文件d:文件夹

链接文件l :快捷方式

管道文件p:用来存储进程间通信的数据

块设备文件b:硬盘,U盘

字符设备文件c:液晶屏文件,键盘,鼠标

套接字文件:用来存储进程间通信的数据

4)文件描述符

在一个进程中,每打开一个文件,系统内核会返回该文件的一个编号,这个编号就叫该文件的描述符,它是一个非负的正整数。

默认是1024.在同一个进程中,文件描述符是不能重复的。

5) 文件的操作流程

#include <unistd.h>

  1. 打开文件:

int open(const char *pathname, int flags); //打开一个已存在的文件

int open(const char *pathname, int flags, mode_t mode);//创建一个文件并打开

2.读写文件:

ssize_t write(int fd, const void *buf, size_t count);

ssize_t read(int fd, void *buf, size_t count);

3.关闭文件:

#include <unistd.h>

6) 操作文件的属性数据

#include <sys/types.h>

#include <sys/stat.h>

#include <unistd.h>

int stat(const char *pathname, struct stat *statbuf);

int fstat(int fd, struct stat *statbuf);

int lstat(const char *pathname, struct stat *statbuf);

7)目录文件的操作

主要包括:创建目录,删除目录,遍历目录,,切换目录,获取当前的目录路径等

创建目录:int mkdir(const char *pathname, mode_t mode); 或者system("mkdir yang");

删除目录:int rmdir(const char *pathname);

遍历目录:

DIR *opendir(const char *name);打开目录

struct dirent *readdir(DIR *dirp);

int closedir(DIR *dirp);

切换目录:int closedir(DIR *dirp);

获取当前目录: char *getcwd(char *buf, size_t size);

检测当前文件是否存在:int access(const char *pathname, int mode);

8)其他函数:

int fcntl(int fd, int cmd, ... /* arg */ );

9)系统IO和标准IO的区别

系统IO主要应用在特定的平台上开发程序

标准IO可以跨平台使用

============================================================================================

三、进程与线程

3.1 进程简介

1)什么是进程

进程就是一个正在运行的程序以及它所依赖资源的总称,它是系统进程资源分配的基本单位。 ***

进程与程序的关系

程序是进程的执行文本,进程是程序的执行过程。 一个程序可以对应多个进程,一个进程对应一个程序。

2)如何查看进程

linux: ps -e 或者top

windows:任务管理器

3)如何标识一个进程

在计算机中,每启动一个进程,系统会给改进程分配一个id号(>=0 0~65535),进程描述符。

获取当前进程的id号:getpid()

获取当前进程父进程的id: getppid()

0~2这三个号时特殊的进程ID,一般不要关闭。

  1. 进程控制块PCB

系统每启动一个进程,内核都会给该进程在内核中创建一个结构体来描述该进程,系统通过这个结构体来感知进程的存在。

5)进程特点

1.独立性

2.并发性

3.异步性

4.动态性

6)进程的状态

就绪:除了cpu以外资源没有获得的进程

运行:进程获得了CPU资源

阻塞:进程需要等待外部的一个事件,如果等到了 就重新放到就绪队列。

7)创建一个进程

  1. fork(); //用来创建一个进程,用户调用一次,返会两次,在父进程空间返回子进程的ID号在子进程空间返回为0.

2.vfork();//x,父进程和子进程共享数据区

3.system();//多用来执行一个外部的命令。system("ls");,特点:就是会启动一个新的进程来执行你指定的命令程序,命令进程在没有执行完之前会阻塞当前进程

#include "unistd.h"

#include "stdio.h"

int main()

{

printf("start\n");

system("ping 127.0.0.1");

printf("end\n");

}

4)exec函数族 (夺舍函数)--将当前的进程的内存四区用你指定程序的内存四区对其做替换,相当于当前进程重获新生。

#include "unistd.h"

#include "stdio.h"

#include "sys/types.h"

int main()

{

printf("start\n");

execl("/bin/ls","ls","-l",NULL);

printf("sdfsdf\n"); //它不会被打印,因为被ls程序覆盖了当前进程的内存四区。

printf("end\n");

}

8)进程的等待

wait() //它会阻塞调用进程,知道等待的子进程结束 就会解除阻塞,还可以回收子进程在内核中所占的资源。

waitpid()

僵尸进程:子进程先于父进程结束,此时父进程没有调用wait函数,这样的子进程叫僵尸进程

孤儿进程:父进程先于子进程结束,此时子进程就变成了孤儿进程,最终孤儿进程被1号进程直接或间接收养。

9)进程的退出

分两种情况:

正常退出(在main函数中遇到return)

在程序的任意函数遇到了exit()或则_exit()

异常退出:abort();

3.2 进程间通信

1)目的:实现数据交换或者实现数据共享或者实现同步或控制。

2)进程间通信的本质:就是在参与通信的进程中,创建一个双方都能看得见的区域(内核)实现通信。

3)通信的方式:

1)管道通信

2)消息队列

3)信号

4)信号量

5)共享内存

6)socket


4)管道通信

作用:实现设备内进程之间的单向通信

特点: 半双工,适合小规模的数据传递,管道的声明周期跟随进程,它传递是是没有结构的数据流。

分类:有名管道和匿名管道。

匿名管道:只能在有亲缘关系的进程之间通信;它的数据存在于内核,不会在文件系统中体现出一个文件

匿名管道的使用流程:

1)创建管道:pipe();

2)读写管道:read()/write()

3)关闭管道close()

有名管道特点:可以在亲缘关系的进程之间和没有关系的亲缘之间都可以实现通信,会在文件系统中体现出一个文件。

流程:

1)先创建管道:int mkfifo(const char *pathname, mode_t mode);

2)打开管道文件:open()

3)读写管道:read()/write()

4)关闭管道close()

5)信号

作用:它也是进程间通信的一种方式,比较简单,它的本质是一个整数。0~63.

特点:通信简单,用户发送的信号是系统提前定义好的,不可以发送没有定义的信号,适合简单通信。

通信时,信息的发送方需要通过信号函数实现对指定信号的发送,常用的信号函数:

#include <sys/types.h>

#include <signal.h>

int kill(pid_t pid, int sig);//通用信号函数

sigqueue() //可以在发送信号的同时携带数据

unsigned int alarm(unsigned int seconds);//专用信号函数 SIGALRM

useconds_t ualarm(useconds_t usecs, useconds_t interval);//秒以下的可以考虑

信号的绑定

#include <signal.h>

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);//实现指定信号与响应函数的绑定。

int sigaction(int signum, const struct sigaction *act, //主要对携带数据的信号进行绑定 该函数经常与 sigqueue()函数进行搭配

struct sigaction *oldact);

流程:

发送信号

绑定信号并响应信号

接收方收到信号之后 对信号的态度:

忽略信号

给信号绑定函数

按照信号默认行为(大部分信号会杀死接收进程)

6)共享内存

特点:适合进程间实现较大数据块传递,它的通信效率是最高的。

缺点:它缺少一种同步的机制,容易造成读写出错。往往可以使用信号量来解决。

使用共享内存进行进程间通信的基本流程:

1)通信双方的一方负责创建共享内存,另一方获取共享内存 (靠一个key值):shmget() IPC_CREAT

int shmget(key_t key, size_t size, int shmflg);//shmflg=IPC_CREAT表示创建

key_t key=ftok("/bin/ls",1);//通过ftok函数来生成KEY值,注意的是要确保通信双方的key一样,这样才能保证我们找的是内核中同一个内存块。

2)建立映射 (将内核中申请的内存块的首地址存放到当前的进程中):shmat();

3)读写共享内存:(和过去操作堆内存是一样):通过指针或内存的操作函数比如memcpy

  1. 断开映射:shmdt();

  2. 销毁共享内存:shmctl(shmid,IPC_RMID,NULL);

6)信号量

特点: 信号量的本质是一个计数器,是一个整数,它不是以传递数据为目的,而是给临界资源提供一种访问秩序。

信号量的操作:

P操作:申请资源,做减法 说名服务能力减弱了

V操作:释放资源 做加法,说明我的服务能力增强了

一般要成对出现。

信号量的使用流程:

1)创建信号量或获取信号量 //int a;--- semget() semflg=IPC_CREAT代表创建 没有代表获取

int semget(key_t key, int nsems, int semflg);//创建一个指定数量(nsems)元素的整数数组

key值也是有ftok()函数来生成

2)初始化信号量 //a=1;----semctl(); cmd=SETVAL 设置信号量的值

3)PV操作 //a++,a-- --semop()

4)销毁信号量 //释放变量a内存资源 // -semctl()

int semctl(int semid, int semnum, int cmd, ...);

cmd=IPC_RMID代表销毁

信号量的分类:

二元信号量:一般做互斥锁,实现互斥访问临界资源

非二元信号量 比如用在同时下载电影的数量方面,并发规模限制

7)消息队列

特点:1.发送的带结构的消息 2.接收方可以有选择的读取感兴趣消息。

场景:适合两个进程需要频繁进行数据交换的场景。

消息队列发送数据前,需要先封装消息

struct mymsg

{

long msgType;//消息类型

type msg_body;//消息体

};

消息队列的流程:

1)创建消息队列:msgget();

2)消息发送与接收//msgsnd/msgrecv()

nt msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,

int msgflg);

3)销毁消息队列

cmd=IPC_RMID代表销毁

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,

int msgflg);

8)套接字通信

特点:可以跨设备或主机实现两者之间的通信

分两种情况:基于TCP/ip 基于UDP/ip

分两个角色:服务器 客户端

TCP/ip 服务器的构建流程:

1)创建套接字 :socket(AF_INET,SOCK_STREAM,0);

2)绑定地址: bind();

3)监听:listen();

4)接收链接:accpet(serid,NULL,NULL);

5)读写数据:read/write()

6)关闭: close()

TCP/ip 客户端:

1)创建套接字:socket()

2)发送链接:connect()

3)收发数据:read/write()

4)关闭:close()


udp/ip 服务器的构建流程:

1)创建套接字socket(AF_INET,SOCK_DGRAM,0);

2)绑定地址:bind()

3)读写数据: :sendto/recvfrom()

4)关闭:close()

udP/ip 客户端:

1)创建套接字

2)收发数据

3)关闭

如何选择TCP/UDP?

1.如果看重可靠性 选择TCP,如果看重速度 选择UDP

3.3 线程

1)什么是线程thread**

线程就是进程中的一个执行流,同时它也是系统进行任务调度的最小单元。

2)线程与进程区别**

进程是线程的容器,一个进程中可以包含多线程

线程间通信比进程间通信更节省资源

同一个进程内的线程间通信比进程间通信更容易

线程更容易来发挥电脑的多核性能。

?协程

3) 包含pthread.h

4)线程的基本使用流程:

1.先定义线程的任务函数:void *task(void *arg){}

2.创建线程:pthread_create(&tid,NULL,task,NULL);

//形参1:做返回值,返回所建线程的ID号

形参2:设置线程的属性信息 ,比如分离属性 pthread_detach()

形参3:指定线程要干的活

形参4:线程的任务函数所需要的数据,如何没有数据提供够可以设置NULL

3.如何没有设置分离属性,pthread_join回收线程在内核所占的资源。(不是必须的)

5)线程退出

当线程函数遇到了最后一条语句就会退出

如果在线程函数中碰到: pthread_exit();

  1. 获取当前线程的id号: pthread_self();

  2. 如何来回收线程的资源:pthread_join() //阻塞主线程,实现父子线程同步;回收子线程的资源;获取子线程结束时的返回值。

8)线程取消:

发送线程的取消信号: pthread_cancel(线程ID);

设置取消的类型:pthread_setcanceltype

  1. 线程间的通信:全局变量或堆内存

10)线程间的互斥

线程的互斥锁

作用:当多个线程要操作一个公共的资源,需要加锁保护,否则容易将数据改乱。

流程:

1.定义互斥锁变量:pthread_mutex_t lock;

2.初始化锁变量:pthread_mutex_init();

3.使用锁变量:

pthraed_mutex_lock();

pthread_mutex_unlock();

4.销毁锁变量:pthread_mutex_destroy();

线程的读写锁: 在读的频次比写的频次高的场景 推荐使用

1.定义变量: pthread_rwlock_t lock

  1. 初始化锁变量 pthread_rwlock_init();

3.使用锁变量:

上读锁:pthread_rwlock_rdlock();

上写锁:pthread_rwlock_wrlock();

解锁pthread_rwlock_unlock();

4.销毁锁变量:pthread_rwlock_destroy();

11)线程间的同步

用全局变量实现同步,还可以用条件变量做

通过条件变量实现线程间的同步(构建多个线程执行的先后顺序)

基本流程:

1.定义条件变量:pthread_cond_t cond;

2.对条件变量做初始化:pthread_cond_init(); 注意:条件变量在使用时需要互斥锁的包含

3.使用条件变量: pthread_cond_wait();//条件变量等待

唤醒:pthread_cond_signal()

pthread_cond_broadcast();

4.销毁条件变量:pthread_cond_destroy();

五、网络编程

1)作用:主要解决设备之间的联网通信问题。

2)为了解决网络中不同设备间的通信问题,因此 有一些组织就制定了关于网络的各种协议。

代表:TCP,UDP,ip .

  1. ip协议

ip属于网络层的协议,它的职责是实现网络中不同节点之间的数据搬运。

ip协议中有一个重要的组成部分:ip地址,主要用来定位设备在网络中的位置。

ip协议:

IPV4协议 ipv6协议

192.168.157.1

fe80::cc39:137c:7c7f:168d

windows: ipconfig

linux: ifconfig

ip地址分:内网IP,公网IP

4)域名

域名的本质就是IP地址的别名 www.baidu.com

5)TCP协议

TCP协议是一种属于传输层协议它给数据传输提供一种保障机制。

TCP协议中有一个重要的内容:端口

端口:因为ip地址可以定位到网络中的某个计算机,但是这个计算机在某个时刻,上边跑了很多程序,网卡不知道将数据交

给那一个上层的应用程序,因此需要一个端口来指定。

端口:0~65535 查看端口:netstat

相同协议 端口不能一样,不同协议端口可以一样。 >=1024

8)udp协议

1.TCP与UDP协议的区别 ***

从可靠性方面:TCP比UDP可靠。

连接:TCP是面向连接的一种协议,UDP是面向无连接协议

TCP支持一对一通信,UDP支持 一对一,一对多通信,多对多。

9)网络字节序

主要规定数据在传输的时候 是先高位还是先发地位。

大端模式。

#include "arpa/inet.h"

htons();//将本机字节序转换成网络字节序

inet_addr();//可以将一个字符串形式的IP地址转换成网络字节序。

10)网络架构模型 ***

OSI七层模型: 应用层,表示层,会话层,传输层,网络层,数据链路层,物理层,

TCP/ip的四层模型:应用层,传输层,网络层,网络接口层

11)套接字socket

套接字就是对通信节点的抽象描述,一般有两部分组成:ip+端口

套接字类型:

流式套接字:SOCK_STREAM ---tcp

数据报套接字:SOCK_DGRAM---UDP

原始套接字:SOCK_RAM

12)TCP/ip通信

13)udp/ip通信

14)并发通信

多线程,多进程,IO复用

void * talk(void *arg)

{

int clifd=*((int*)arg);

while(1)

{

//聊天逻辑

read(clifd,buff,100);

}

}

int main()

{

int serfd=socket(AF_INET,SOCK_STREAM,0);

struct sockaddr addr;

addr.sin_family=AF_INET;

addr.sin_port=htons(8000)

addr.sin_addr.s_addr=inet_addr("127.0.0.1");

bind(serfd,&addr);

listen(serfd,10);

while(1)

{

int clifd=accept(serfd,NULL,NULL);

pthread_create(&tid,NULL,talk,&clifd);

pthread_detach(tid);//设置

}

}

15)IO复用

1.什么IO复用

用单个线程或进程来监听多个文件描述符,一旦有描述符准备就绪 对其进行处理。

2.IO复用的技术

select poll epoll

3。实现IO复用的基本流程

1.创建套接字并绑定监听

2.创建一个容器来保存你要监控的文件描述符对象集合

3.将监听套接字添加到监控集合中

4.开始进行监控

5.如果是监听套接字有数据到来 调佣accept进行接收,如果不是监听套接字,则调用read()进行读取

6.关闭

man 7 epoll

#define MAX_EVENTS 10

struct epoll_event ev, eventsMAX_EVENTS;

int listen_sock, conn_sock, nfds, epollfd;

1.创建套接字并绑定监听

/* Code to set up listening socket, 'listen_sock',

(socket(), bind(), listen()) omitted */

创建一个容器(红黑树)

epollfd = epoll_create1(0);

if (epollfd == -1) {

perror("epoll_create1");

exit(EXIT_FAILURE);

}

将监听套接字添加到监控集合中

ev.events = EPOLLIN;

ev.data.fd = listen_sock;

if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {

perror("epoll_ctl: listen_sock");

exit(EXIT_FAILURE);

}

开始进行监控

for (;;) {

nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);

if (nfds == -1) {

perror("epoll_wait");

exit(EXIT_FAILURE);

}

如果是监听套接字有数据到来 调佣accept进行接收,如果不是监听套接字,则调用read()进行读取

for (n = 0; n < nfds; ++n) {

if (eventsn.data.fd == listen_sock) {

conn_sock = accept(listen_sock,

(struct sockaddr *) &addr, &addrlen);

if (conn_sock == -1) {

perror("accept");

exit(EXIT_FAILURE);

}

setnonblocking(conn_sock);//设置客户端套接字为非阻塞模式 fcntl ioctl

ev.events = EPOLLIN | EPOLLET;

ev.data.fd = conn_sock;

if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,

&ev) == -1) {

perror("epoll_ctl: conn_sock");

exit(EXIT_FAILURE);

}

} else {

do_use_fd(eventsn.data.fd);//实现读取的一个函数

}

}

}

相关推荐
Benszen1 小时前
云计算基础-13:Linux网络管理实战-1
linux·运维·云计算
扬帆破浪1 小时前
文档自动检查:用 Claude Code、OpenClaw 审 WPS 文件:预览、批注、改正文
运维·服务器·wps
zt1985q2 小时前
本地部署开源网络书签与内容管理工具 Karakeep 并实现外部访问
运维·服务器·网络·数据库·网络协议·开源
具身智能进化论2 小时前
协作机器人产业进入规模化部署期,未来几年的增长从何而来
大数据·运维·人工智能·机器人·自动化·工厂方法模式
yunyi4 小时前
改个 SSH 端口,我把自己锁在了服务器外面
运维·后端
九硕智慧建筑一体化厂家6 小时前
从电费支出到碳资产收益,直流照明如何让商业楼宇实现柔性用能
运维·人工智能·智慧城市
科技园长6 小时前
复杂厂区车辆高精度调配:UWB追踪技术底座与选型格局重构
运维·重构
2601_949499946 小时前
硬件工程师实操:芯瑞科技两款 400G 高速互联方案参数、场景、选型全解析
大数据·运维·人工智能·科技·光模块
雾时之林6 小时前
Linux--介绍及管理
linux·运维·服务器