二阶段总结

一、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);//实现读取的一个函数

}

}

}

相关推荐
Apipi*几秒前
30天速通Linux 第九章Linux网络编程
linux·运维·网络
天远数科14 分钟前
零信任架构实战:基于天远风控经营异常预警构建自动化电子签章前置合规网关
运维·人工智能·架构·自动化
Linux-lucky25 分钟前
43-Linux学习之旅之Shell 脚本(二)
linux·运维·学习·ubuntu·dash
YH行业报告分析2 小时前
2026电缆铺设设备市场:自动化敷设与电网升级驱动增长
运维·自动化
ajassi20003 小时前
AI语音智能体开发日记(十八)智能体服务器xiaozhi-esp32-server源码部署指南
运维·服务器·人工智能·ai·ai编程
‎ദ്ദിᵔ.˛.ᵔ₎3 小时前
linux 进程信号(下)
linux·运维·服务器
Mikko74 小时前
Linux 磁盘满了但 du 找不到?LVM 扩容 df 不变?fstab 写错开不了机?三台真机实测磁盘排查
linux·运维·centos
蓝胖的四次元口袋4 小时前
Jenkins知识梳理(2)
运维·jenkins
yumgpkpm4 小时前
Acceldata ODP 3.3.6.4 vs CDP Private Cloud Base 7.3.2 对比
大数据·运维·服务器·hadoop·华为·zookeeper·hbase
Liuqy-054 小时前
特殊进程——孤儿、僵尸、守护进程
linux·运维·服务器