linux系统(守护进程-线程)08_守护进程,线程,线程创建,线程退出,线程回收,杀死线程,线程分离,线程属性分离

news2024/11/25 10:53:13

01 学习目标

1.守护进程的特点
2.熟练进行守护进程的创建
3.熟练掌握多线程的创建
4.熟练掌握线程的退出和资源回收

02 守护进程相关的概念

进程组:多个进程在同一个组,第一个进程默认是进程组的组长。

会话:进程组的更高一级,多个进程组对应一个会话。

创建会话需要注意以下5点注意事项:

  1. 调用进程不能是进程组组长,该进程变成新会话首进程(session header)
  2. 该进程成为一个新进程组的组长进程
  3. 新会话丢弃原有的控制终端,该会话没有控制终端。
  4. 该调用进程是组长进程,则出错返回
  5. 建立新会话时,先调用fork,父进程终止,子进程调用setsid

创建会话的时候,组长不可以创建,必须是组员创建
创建会话的步骤:创建子进程,父进程去死,子进程自当会长。

man setsid

守护进程:
Deamon(精灵)进程,是Linux中的后台服务进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。一般采用以d结尾的名字。
Linux后台的一些系统服务进程,没有控制终端,不能直接和用户交互,不受用户登录,注销的影响,一直在运行着,他们都是守护进程。如:预读入缓输出机制的实现;ftp服务;nfs服务器等。
创建守护进程,最关键的一步是调用setsid函数创建一个新的Session,并成为Session Leader.

守护进程的步骤:

  1. 创建子进程,父进程退出
    所有工作在子进程中进行形式上脱离了终端控制

  2. 在子进程中创建新会话
    setsid()函数
    使子进程完全独立出来,脱离控制

  3. 改变当前目录为根目录
    chdir()函数
    防止占用可卸载的文件系统
    也可以换成其它路径

  4. 设置文件权限掩码
    umask()函数
    防止继承的文件创建屏蔽字拒绝某些权限
    增加守护进程灵活性

  5. 关闭文件描述符
    继承的打开文件不会用到,浪费系统资源,无法卸载

  6. 开始执行守护进程核心工作

  7. 守护进程退出程序模型

简化版步骤:

  • 创建子进程fork
  • 父进程退出
  • 子进程当会长setsid
  • 切换工作目录$HOME
  • 设置掩码umask
  • 关闭文件描述符0,1,2,为了避免浪费资源
  • 执行核心逻辑
  • 退出

03 守护进程创建

创建一个守护进程:每分钟在$HOME/log/ 创建一个文件 程序名.时间戳

#include<stdio.h>
#include<unisstd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcnt1.h>
#include<string.h>
#include<stdlib.h>

#define _FILE_NAME_FORMAR_ "%s/log/mydaemon.%ld" //定义文件格式化

void touchfile(int num)
{
	char *HomeDir = getenv("HOME");
	char strFilename[256]={0};

	sprintf(strFilename,_FILE_NAME_FORMAR_,HomeDir,time(NULL));
	
	int fd=open(strFilename,O_RDWR|O_CREAT,0666);
	if(fd<0)
	{
		perror("open err");
		exit(1);
	}
	close(fd);
}
int mian()
{
	//创建子进程,父进程退出
	pid_t pid=fork();
	if(pid>0)
		exit(1);
		
	//当会长
	setsid()
	
	//设置掩码
	umask(0);
	//切换目录
	chdir(getenv("HOME"));//切换到家目录
	//关闭文件描述符
	//close(1),close(0),close(2);
	//执行核心逻辑
	struct itimerval myit={{60,0},{60,0}};
	setitimer(ITIMER_REAL,&myit,NULL);
	struct sigaction act;
	act.sa_flags=0;
	sigemptyset(&act.sa_mask);
	act.sa_handler=touchfile;
	
	sigaction(SIGALRM,&act,NULL);
	while(1)
	{
		//每隔1分钟在/home/itheima/log下创建文件
		sleep(1);
		//do sth
	}

	//退出
	return 0;
}

# 加2图
关闭进程,此线程依旧在运行
在这里插入图片描述

04 守护进程拓展了解

通过nohup指令也可以达到守护进程创建的效果

nohup cmd[>1.log] &

  • nohup 指令会让cmd收不到SIGHUP信号
  • & 代表后台运行
#include<stdio.h>
#include<unisstd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcnt1.h>
#include<string.h>
#include<stdlib.h>

int main(int argc,char* argv[])
{
	char strFileName[256]={0};
	
	while(1)
	{
		memset(strFileName,0x00,sizeof(strFileName));
		sprintf(strFilename,"%s/log/zhen2.%ld",getenv("HOME"),time(NULL));
		int fd=open(strFilename,O_RDWR|O_CREAT,0664);
		if(fd<0)
		{
			perror("open err");
			exit(1);
		}
		close(fd);
		sleep(5);
	}
}

# 加图
在这里插入图片描述
在这里插入图片描述

05 线程有关的概念

在这里插入图片描述
# 加图
线程man page安装

sudo apt-get install manpages-posix-dev

线程的概念:轻量级的进程,一个进程内部可以有多个线程,默认情况下一个进程只有一个线程

线程是最小的执行单元,进程是最小的系统资源分配单元。
内核实现都是通过clone函数实现的。
线程也有PCB,但没有独立的地址空间(共享)。

Linux内核线程实现原理
类Unix系统中,早期是没有“线程”概念的,80年代才引入,借助进程机制实现出了线程的概念,因此在这类系统中,进程和线程关系密切。

  1. 轻量级进程(ligth-weight-process),也有PCB,创建线程使用的底层函数和进程一样,都是clone
  2. 从内核里看进程和线程是一样的,都是各自不同的PCB
  3. 进程至少有一个线程
  4. 线程可看做寄存器和栈的合集
  5. 在linux下,线程是最小的执行单元,进程是最小的分配资源单元。

查看LWP号:ps -Lf pid 查看指定线程的lwp号。
# 加图

06 线程的优点和缺点

线程共享资源

  1. 文件描述符表
  2. 每种信号的处理方式
  3. 当前工作目录
  4. 用户ID和组ID
  5. 内存地址空间(.text/.data/.bss/heap共享库)

线程非共享资源

  1. 线程id
  2. 处理器线程和栈指针(内核栈)
  3. 独立的栈空间(用户空间栈)
  4. errno变量
  5. 信号屏蔽字
  6. 调度优先级

# 加图
errno变量,获得错误码对应的错误信息

char* strerror(int errnum);

线程的优点:

  • 提供并发性
  • 占用资源小
  • 通信方便

线程的缺点:

  • 调用困难
  • 库函数,不稳定
  • 对信号支持不好

07 pthread_create创建一个线程

man pthread_create

创建一个线程

int pthread_create(pthread_t* thread,const pthread_attr_t* attr,void* (*start_routine)(void*),void* arg);
  • thread 线程的id,传出参数
  • attr 代表线程的属性
  • 第三个参数 函数指针,void* fun(void*)
  • arg 线程执行函数的参数
  • 返回值
    成功返回0
    失败返回errno

编译的时候需要加pthread库

Compile and link with -pthread

gcc 03_pthread_create.c -lpthread
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>

void* thr(void *arg)
{
	printf("I am a thread!pid=%d,tid=%lu\n",getpid(),pthread_self());
	return NULL;
}

int main()
{
	pthread_t tid;
	pthread_create(&tid,NULL,thr,NULL);
	printf("I am a main thread!pid=%d,tid=%lu\n",getpid(),pthread_self());
	sleep(1);
	return 0;
}

# 加图

echomake

多一个makefile
在这里插入图片描述

alias echomake

添加命令到makefile
在这里插入图片描述
# 加图

在家目录的.bashrc增加

alias echomake='cat ~/bin/makefile.template>>makefile'

~/bin/makefile.template 是模板makefile

为了将模板makefile重定向到makefile文档,可以修改makefile文档,在重新执行makefile。
在这里插入图片描述
设置shell里vi 的快捷键

set -o vi

设置后vi的快捷键在shell里都能使用

08 pthread_exit 线程的退出

pthread_exit 线程退出函数
注意事项:

  • 在线程中使用pthread_exit
  • 在线程中使用return(主控线程return代表退出进程)
  • exit代表退出整个进程
#include<stdio.h>
#include<unistd.h>
#include<pthread.h>
#include<stdlib.h>

void* thr(void *arg)
{
	printf("I am a thread!pid=%d,tid=%lu\n",getpid(),pthread_self());
	//return NULL;
	pthread_exit(NULL);
	exit(1);
}

int main()
{
	pthread_t tid;
	pthread_create(&tid,NULL,thr,NULL);
	printf("I am a main thread!pid=%d,tid=%lu\n",getpid(),pthread_self());
	
	sleep(10);
	printf("I will out\n");
	pthread_exit(NULL);
	return 0;
}

在这里插入图片描述

09 pthread_join线程的回收

pthread_join 线程回收函数

int pthread_join(pthread_t thread,void **retval);

  • thread 创建的时候传出的第一个参数
  • retval 代表的传出线程的退出信息

pthread_rtn.c:

#include<stdio.h>
#include<unistd.h>
#include<pthread.h>

void* thr(void *arg)
{
	printf("I am a thread,tid=%lu\n",pthread_self());
	return (void*)100;
}

int main()
{
	pthread_t tid;
	pthread_create(&tid,NULL,thr,NULL);
	void *ret;
	pthread_join(tid,&ret);//线程回收
	
	printf("ret exit with %d\n",(int)ret);
	pthread_exit(NULL);
	return 0;
}

在这里插入图片描述

10 pthread_cancel杀死线程

pthread_cancel 杀死线程

int pthread_cancel(pthread_t thread)

  • 需要出入tid
  • 返回值
    失败返回errno
    成功返回0

被pthread_cancel杀死的线程,退出状态为PTHREAD_CANCELED
#define PTHREAD_CANCELED((void *)-1)

pthread_cancel.c:

#include<stdio.h>
#include<unistd.h>
#include<pthread.h>

void* thr(void *arg)
{
	while(1)
	{
		printf("I am a thread,very happy!tid=%lu\n,pthread_self());
		sleep(1);
	}
	return NULL;
}

int main()
{
	pthread_t tid;
	pthread_create(&tid,NULL,thr,NULL);
	
	sleep(5);
	pthread_cancel(tid);//杀死线程
	void *ret;
	pthread_join(tid,&ret);
	printf("thread exit with %d\n",ret);
	return 0;
}

在这里插入图片描述
但是循环中,无sleep,无法杀死

pthread_cancel.c:

#include<stdio.h>
#include<unistd.h>
#include<pthread.h>

void* thr(void *arg)
{
	while(1)
	{
		//printf("I am a thread,very happy!tid=%lu\n,pthread_self());
		//sleep(1);
	}
	return NULL;
}

int main()
{
	pthread_t tid;
	pthread_create(&tid,NULL,thr,NULL);
	
	sleep(5);
	pthread_cancel(tid);//杀死线程
	void *ret;
	pthread_join(tid,&ret);
	printf("thread exit with %d\n",ret);
	return 0;
}

遇上上述情况,可采用如下方法:
强制设置取消点

pthread_testcancel()

11 pthread_detach线程分离

线程分离状态:指定该状态,线程主动与主控线程断开关系。线程结束后,其退出状态不由其他线程获取,而直接自己自动释放。网络,多线程服务器常用。

线程分离

int pthread_detach(pthread_t thread);

此时不需要pthread_join回收资源

pthread_detach.c:

#include<stdio.h>
#include<unistd.h>
#include<pthread.h>

void *thr(void *arg)
{
	printf("I am a thread,self=%lu\n",pthread_self());
	sleep(4);
	printf("I am a thread,self=%lu\n",pthread_self());
	return NULL;
}

int main()
{
	pthread_t tid;
	pthread_create(&tid,NULL,thr,NULL);
	
	pthread_detach(tid);//线程分离
	sleep(5);
	int ret=0;
	if((ret=pthread_join(tid,NULL))>0)
	{
		printf("join err:%d,%s\n",ret,strerror(ret));
	}
	return 0;
}

在这里插入图片描述

12 线程属性设置分离

pthread_equal 比较两个线程ID是否相等。

int pthread_equal(pthread_ t t1,pthread_t t2)

线程的特点:

  • 线程共享全局变量
  • 线程id在进程内部是唯一的,但不是全局唯一的!

进程的属性控制:
初始化线程属性

int pthread_attr_init(pthread_attr_t *attr)

销毁线程属性

int pthread_attr_destory(pthread_attr_t *attr)

设置属性分离态

int pthread_attr_setdetachstate(pthread_attr_t* attr,int detachstate)
  • attr init初始化的属性
  • detachstate
    PTHREAD_CREATE_DETACHED 线程分离
    PTHREAD_CREATE_JOINABLE 允许回收

pthread_attr.c:

#include<stdio.h>
#inlclude<unistd.h>
#include<pthread.h>

void *thr(void *arg)
{
	printf("I am a thread\n");
	return NULL;
}

int main()
{
	pthread_attr_t attr;
	pthread_attr_init(&attr);//初始化属性;

	pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);//设置属性分离
	pthread_t tid;
	pthread_create(&tid,&attr,thr,NULL);
	
	if((ret=pthread_join(tid,NULL))>0)
	{
		printf("join err:%d,%s\n",ret,strerror(ret));
	}
	pthread_attr_destroy(&attr);//摧毁属性
	return 0;
}

在这里插入图片描述

13 线程注意事项

NPTL:

  1. 查看当前pthread库版本getconf GUN_LIBPTHREAD_VERSION
    在这里插入图片描述

  2. NPTL实现机制(POSIX),Native POSIX Thread Library

  3. 使用线程库时gcc指定 -lpthread

线程使用注意事项

  1. 主线程退出其他线程不退出,主线程应调用pthread_exit
  2. 避免僵尸线程
    pthread_join
    pthread_detach
    pthread_create指定分离属性
    被join线程可能在join函数返回前就释放完自己的所有内存资源,所有不应当返回被回收线程栈中的值。
  3. molloc和mmap申请的内存可以被其他线程释放
  4. 应避免在多线程模型中调用fork,除非,马上exec,子线程中只有调用fork的线程存在,其他线程在子进程中均pthread_exit
  5. 信号的复杂语义很难和多线程共存,应避免在多线程引入信号机制。

创建多少个线程?
cpu核数*2+2

14 作业

1.实现一个守护进程,每分钟写入一次日志,要求日志文件保存在$HOME/log/下

  • 命名规则:程序名.yyyymm
  • 写入内容格式:mm:dd hh:mi:ss 程序名[进程号]:消息内容
#include<string.h>
#include<stdlib.h>
#include<sys/time.h>
#include<time.h>
#include<signal.h>

#define _PROGRAM_NAME_ "touchevety"
#define _LOG_FORMAT_ "%02d-%02d %02d:%02d:%02d %s [%06d]:%s\n" //mm-dd hh:mi:ss programname [pid]:msg
#define _LOG_FILE_ "%s/log/%s.%04f%02d"  //$HOME/log/programname.yyyymm,如果$HOME/log不存在,需要创建

void catch_alarm(int num)
{
	time_t nowtime=time(NULL);
	struct tm* nowtm=localtime(&nowtime);

	char strLogFile[100];
	memset(strLogFile,0x00,sizeof(strLogFile));
	sprintf(strLogFile,_LOG_FILE,getenv("HOME"),_PROGRAM_NAME_,nowtm->tm_year+1900,nowtm->tm_mon+1);
	int fd =open(strLogFile,O_WRONLY|O_CREAT|O_APPEND,0666);
	if(fd<0)
	{
		perror("open file err");
		printf("file is %s\n",strLogFile);
		exit(1);
	}
	char buf[2014]={0};
	sprintf(buf,_LOG_FORMAT_ ,nowtm->tm_mon+1,nowtm->tm_mday,nowtm->tm_hour,nowtm->tm_min,nowtm->tm_sec,_PROGRAM_NAME_,getpid(),"I am alive!");
	write(fd,buf,strlen(buf));
	close(fd);
}

int main()
{
	//初始化需要的环境变量
	char *strHomeDir = getenv("HOME");
	printf("homedir is %s\n",strHomeDir);
	//守护进程创建
	pid_t pid = fork();
	if(pid>0)
	{
		exit(1);//父进程退出
	}
	setsid();//子进程当会长,此上2步必须
	umask(0);//设置掩码
	chdir(strHomeDir);
	close(0);
	
	//设置信号捕捉,捕捉ALARM信号
	struct sigaction act;
	sigemptyset(&act.sa_mask);
	act.sa_flags=0;
	act.sa_handler=catch_alarm;
	sigaction(SIGALRM,&act,NULL);

	//设置时钟参数
	struct itimerval myit={{60,0},{1,0}};//每隔60s来一次闹钟
	setitimer(ITIMER_REAL,&myit,NULL);
	
	//循环等待
	while(1)
	{
		sleep(120);
	}
	return 0;
}

2.实现多线程拷贝

#include<pthread.h>
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<fcnt1.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/mman.h>

#define _THR_CNT_ 5 //线程数

typedef struct _TaskInfo{
	int num;
	void *src;
	void *des;
	int size'
}TaskInfo;

void *thr_cp(void *arg)
{
	TaskInfo *info=arg;
	int num=info->num;
	int cpsize=info->size/_THR_CNT_;
	int mod=info->size%cpsize;
	if(num=_THR_CNT_-1)
	{
		memcpy(info->des+num*size,info->src+num*cpsize,cpsize+mode);
	}
	else
	{
		memcpy(info->des+num*size,info->src+num*cpsize,cpsize);
	}
	return NULL;
}

int main(int argc,char *argv[])
{
	if(argc!=3)
	{
		printf("./a.out srcfile desfile\n");
		return -1;
	}
	int n=_THR_CNT_;//线程个数
	struct stat sb;
	if(stat(argv[1],&sb)<0)
	{
		perror(argv[1]);
		exit(1);
	}
	long lfilesize=sb.size;
	int fdsrc=open(argv[1].O_RDONLY);
	int fddes=open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0666);
	ftruncate(fddes,lfilesize);
	if(fdsrc<0||fddes<0)
	{
		printf("open file %s %s err\n",argv[1],argv[2]);
		exit(1);
	}
	
	void *srcmem=mmap(NULL,lfilesize,PROT_READ,MAP_PRIVATE,fdsrc,0);
	if(srcmem==MAP_FAILED)
	{
		perror("mmap srcfile err");
		eixt(1);
	}
	
	void *desmem=mmap(NULL,lfilesize,PROT_READ|PROT_WRITE,MAP_SHARED,fddes,0);
	if(desmem==MAP_FAILED)
	{
		perror("mmap desfile err");
		eixt(1);
	}
	
	TaskInfo taskInfos[_THE_CNT_];
	pthread_t tid[_THE_CNT_];
	int i;
	for(i=0;i<n;i++)
	{
		taskInfos[i].src=srcmem;
		taskInfos[i].des=desmen;
		taskInfos[i].num=i;
		taskInfos[i].size=lfilesize;
		pthread_create(&tid[i],NULL,thr_cp,&taskInfos[i]);
	}
	for(i=0;i<n;i++)
	{
		pthread_join(tid[i],NULL);
	}
	munmap(srcmem,lfilesize);
	munmap(desmem,lfilesize);
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/534637.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

二分搜索帮我解决实际问题

文章阅读 文章阅读 二分搜索代码框架常应用于「在有序数组中搜索指定元素」这个基本场景&#xff0c;具体的实际问题可以没有那么直接&#xff0c;但是也可以使用二分搜索进行解决 实际问题一般都让你求最值&#xff0c;比如让你求吃香蕉的「最小速度」&#xff0c;让你求轮船…

JavaWeb《后端内容:4. 项目实战:书城系统》

0.把之前的系统的Myssm部分封装为包 新建空的工件jar包 创建相同的文件夹&#xff0c;导入相应文件要从out里面导入字节码文件 全部设置完后点构建工件&#xff0c;然后点刚刚我们设置的这个工件&#xff0c;然后从src下的out文件相应的位置就能找到这个jar包&#xff0c;用于…

Discourse Table Builder 插件

ABC 仓库链接GitHub - discourse/discourse-table-builder: A theme component that allows for easily building and editing of markdown tables in Discourse. 概述Table Builder 这个插件能够让用户能够在 Discourse 的编辑对话框中快速的对表格进行编辑&#xff0c;同时也…

Flink学习——处理不同数据源的流数据,存入不同的Sink端

目录 一、单机版安装 二、IDEA操作Flink (一)添加依赖 (二)数据源——Source 1.加载元素数据 2.加载集合数据 3.加载文件目录 4.加载端口 5.加载kafka的topic——重要&常用 6.加载自定义数据源 (三)输出端——Sink 1.读取文件中的数据&#xff0c;处理后输出到…

ESP32设备驱动-振动电机模块驱动

振动电机模块驱动 文章目录 振动电机模块驱动1、振动电机介绍2、硬件准备3、软件准备4、驱动实现1、振动电机介绍 振动电机是用于产生振动的机械装置。 振动的产生是在驱动轴上质量不均匀的电动机的支持下发生的。 它是一种微型直流电机,通过振动让用户知道声音。 在此必须注…

《计算机网络—自顶向下方法》 Wireshark实验(五):UDP 协议分析

用户数据报(UDP)协议是运输层提供的一种最低限度的复用/分解服务&#xff0c;可以在网络层和正确的用户即进程间传输数据。UDP 是一种不提供不必要服务的轻量级运输协议&#xff0c;除了复用/分用功能和简单的差错检测之外&#xff0c;几乎就是 IP 协议了&#xff0c;也可以说它…

RabbitMQ养成记 (10.高级特性:死信队列,延迟队列)

死信队列&#xff08;DLX&#xff09; 这个概念 在其他MQ产品里面也是有的&#xff0c;只不过在Rabbitmq中稍微特殊一点 什么叫私信队列呢&#xff1f; 就是当消息成为 dead message之后&#xff0c;可以重新发到另外一台交换机&#xff0c;这个交换机就是DLX。 注意这里的有翻…

基于OpenCV的人脸检测软件(含Python源码+UI界面+图文详解)

软件功能演示 摘要&#xff1a;人脸检测的目标是找出图像中所有的人脸对应的位置&#xff0c;算法的输出是人脸外接矩形在图像中的坐标&#xff0c;可能还包括姿态如倾斜角度等信息。本文详细介绍了其实现的技术原理&#xff0c;同时给出完整的Python实现代码&#xff0c;并且通…

GeoTools实战指南: 空间坐标系-地理信息科学的核心

GeoTools实战指南: 空间坐标系-地理信息科学的核心 引言 在我们的日常生活中,地图、GPS导航和地理位置服务已经变得司空见惯。但是,你有没有想过这些工具背后的工作原理呢?它们都依赖于一种称为"空间坐标系"的关键概念。本文将深入探讨空间坐标系的基础知识和其…

多线程专题(上)学习随手笔记

JMM&#xff1a;主内存物理内存线程共享&#xff0c;工作内存CPU缓存线程独占volatile&#xff1a;可见性、禁止指令重排&#xff0c;不可保证原子性&#xff1b;用于懒汉单例模式&#xff08;双重检测&#xff09;或状态标记Synchronized&#xff1a;保证代码块或方法同步化执…

Unity的URP下使用SRPBatcher

大家好&#xff0c;我是阿赵。这里继续来讲一下URP相关的东西。 这次主要说的是SRP Batcher的使用 一、在URP下实现SRP Batcher 1、设置 在我们创建的URPAsset文件的高级选项里面&#xff0c;有一个SRP Batcher的开关&#xff0c;默认就是勾上的。 2、修改shader 在把项目转…

小白白也能学会的 PyQt 教程 —— 自定义组件 Switch Button

文章目录 前言思路讲解代码部分 前言 最近在搞 Python 课程设计&#xff0c;想要搞一个好看的 UI&#xff0c;惊艳全班所有人。但打开 Qt Creator&#xff0c;Win7 风格的复古的按钮是在让我难以下手。 其次&#xff0c;我因为想要打造一个 Fluent UI 样式的设置页面&#xff…

详解c++STL—string组件

目录 一、string基本概念 1、本质 2、string和char * 区别&#xff1a; 3、特点&#xff1a; 二、string构造函数 1、构造函数原型 2、示例 三、string赋值操作 1、赋值的函数原型 2、示例 四、string字符串拼接 1、函数原型 2、示例 五、string查找和替换 1、功…

tomcat目录结构

tomcat服务器安装根目录下有很多子目录&#xff0c;这些目录的作用是&#xff1a; (1)bin&#xff1a;存放了tomcat服务器中的可执行的批处理文件(startup.bat shutdown.bat) (2)conf&#xff1a;存放了tomcat相关的配置文件(其中的server.xml是tomcat服务器核心配置文件) …

26. Pandas处理分析网站原始访问日志

Pandas处理分析网站原始访问日志 目标&#xff1a;真实项目的实战&#xff0c;探索Pandas的数据处理与分析 实例&#xff1a; 数据来源&#xff1a;我自己的wordpress博客蚂蚁学Python – 你有没有为写代码拼过命&#xff1f;那你知不知道 人生苦短&#xff0c;我用Python&am…

Python挑选出无Labelme标注文件的图片文件

Python挑选出无Labelme标注文件的图片文件 前言前提条件相关介绍实验环境Python挑选出无Labelme标注文件的图片文件代码实现输出结果 前言 本文是个人使用Python处理文件的电子笔记&#xff0c;由于水平有限&#xff0c;难免出现错漏&#xff0c;敬请批评改正。 (https://blog.…

【设计原则与思想:总结课】38 | 总结回顾面向对象、设计原则、编程规范、重构技巧等知识点

到今天为止&#xff0c;设计原则和思想已经全部讲完了&#xff0c;其中包括&#xff1a;面向对象、设计原则、规范与重构三个模块的内容。除此之外&#xff0c;我们还学习了贯穿整个专栏的代码质量评判标准。专栏的进度已经接近一半&#xff0c;马上就要进入设计模式内容的学习…

Python: 生成ubuntu apt镜像地址

文章目录 1. 目的2. 设计3. 实现4. 调用5. 参考 1. 目的 每次新配置 Ubuntu 系统&#xff0c;免不了配置 apt 源。尽管可以通过 GUI 界面进行选择&#xff0c;但自动化程度不够&#xff0c;不同桌面&#xff08;Unity/Gnome/KDE&#xff09;下的界面也不太一样&#xff1b; 使…

Java基础-sleep和wait的区别

本文介绍Java中sleep和wait方法的使用区别 文章目录 sleep()wait()sleep()和wait()对比区别相同点 sleep() 查看sleep方法&#xff0c;可见其是static native方法 public static native void sleep(long millis) throws InterruptedException;sleep()方法需要指定等待的时间。…

HTTP第14讲——HTTP传输大文件的方法

背景 HTTP 可以传输很多种类的数据&#xff0c;不仅是文本&#xff0c;也能传输图片、音频和视频。 早期互联网上传输的基本上都是只有几 K 大小的文本和小图片&#xff0c;现在的情况则大有不同。网页里包含的信息实在是太多了&#xff0c;随随便便一个主页 HTML 就有可能上百…