Linux下的系统编程——守护进程、线程(十二)

news2024/10/6 6:45:40

前言:

我们知道进程拥有一个PCB,在Linux中被称为task_struct,并且有一个进程地址空间,也有一个页表,通过页表指向物理内存,但是从今天开始,对进程的概念可能发生变化,这个我们后边来说,在Linux中,并没有真正的线程,而是使用进程的PCB来模拟线程,也就是说一个线程在创建时,只会去创建一个PCB,而这个PCB也指向主线程的虚拟地址空间,和其他线程一起共享内存的代码和数据。一个线程也被称为一个执行流,这是因为线程是被CPU调度的执行流,而一个进程就是分配系统资源的基本实体。下面让我们进入线程的学习吧!

目录

一、进程组和会话

*二、守护进程:

1.守护进程:

2.守护进程创建步骤:

​编辑 三、线程

1.三级映射:

2.线程概念:

3.线程共享:

(1)线程共享资源

(2) 线程非共享资源

4.线程优、缺点

5.线程控制原语:

1)获取线程id

2) 创建子线程。

 3)循环创建N个子线程:

4)线程中全局变量共享

5)   退出当前线程

6)   阻塞 回收线程

   7)杀死一个线程。

8)设置线程分离

9)   线程与进程原语比较

6.线程属性:

    


一、进程组和会话

        进程组,也称之为作业。BSD 于 1980 年前后向 Unix 中增加的一个新特性。代表一个或多个进程的集合。每个 进程都属于一个进程组。在 waitpid 函数和 kill 函数的参数中都曾使用到。操作系统设计的进程组的概念,是为了简 化对多个进程的管理。

        当父进程,创建子进程的时候,默认子进程与父进程属于同一进程组。进程组 ID==第一个进程 ID(组长进程)。 所以,组长进程标识:其进程组 ID==其进程 ID

可以使用 kill -SIGKILL -进程组 ID(负的)来将整个进程组内的进程全部杀死。                                 

        组长进程可以创建一个进程组,创建该进程组中的进程,然后终止。只要进程组中有一个进程存在,进程组就存在,与组长进程是否终止无关。

*二、守护进程:

1.守护进程:

        daemon(精灵)进程。通常运行与操作系统后台,脱离控制终端。一般不与用户直接交互。周期性的等待某个事件发生或周期性执行某一动作

        不受用户登录注销影响。通常采用以d结尾的命名方式

**2.守护进程创建步骤:

    1. fork子进程,让父进程终止

    2. 子进程调用 setsid() 创建新会话

    3. 通常根据需要,改变工作目录位置 chdir(), 防止目录被卸载。

    4. 通常根据需要,重设umask文件权限掩码,影响新文件的创建权限。  

        022 -- 755    0345 --- 432   r---wx-w-   422

    5. 通常根据需要,关闭/重定向文件描述符

    6. 守护进程业务逻辑。while()

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/stat.h>
void sys_err(const char *str){
	perror(str);
	exit(1);
}

int main(int argc,char *argv[])
{
	pid_t pid;
	int ret,fd;

	pid = fork();    //创建进程
	if(pid > 0)      //父进程终止
		exit(0);

	pid = setsid();    //创建新会话
	if(pid == -1)
		sys_err("setsid error");
		
	ret = chdir("/home/book/Desktop");    //改变工作目录位置
	if(ret == -1)
		sys_err("chdir error");

	umask(0022);    //改变文件访问权限掩码

	close(STDIN_FILENO);    //关闭文件描述符 0

	fd = open("/dev/null",O_RDWR);	//fd --> 0
	if(fd == -1)
		sys_err("open error");

	dup2(fd,STDOUT_FILENO);    //重定向stdout
	dup2(fd,STDERR_FILENO);    //重定向stderr
	
	while(1);    //模拟守护进程业务

	return 0;
}

用户的登录注销不影响进程

 三、线程

1.三级映射:

三级映射:

        进程 PCB --> 页目录(可看成数组,首地址位于 PCB 中) --> 页表 --> 物理页面 --> 内存单元

2.线程概念:

    进程:有独立的进程地址空间。有独立的pcb。        分配资源的最小单位。

    线程:有独立的pcb。没有独立的进程地址空间。    最小单位的执行。

    ps -Lf 进程id     ---> 线程号。        LWP  ---> cpu 执行的最小单位。

 最小执行单位:

3.线程共享:

        独享栈空间内核栈、用户栈

        共享 ./text./data ./rodataa ./bsss heap  ---> 共享【全局变量】(errno)

(1)线程共享资源

                1).文件描述符表

                2).每种信号的处理方式

                3).当前工作目录

                4).用户 ID 和组 ID

                5).内存地址空间 (.text/.data/.bss/heap/共享库)

(2) 线程非共享资源

                1).线程 id

                2).处理器现场和栈指针(内核栈)

                3).独立的栈空间(用户空间栈)

                4).errno 变量

                5).信号屏蔽字

                6).调度优先级

4.线程优、缺点

        优点: 1. 提高程序并发性   2. 开销小   3. 数据通信、共享数据方便

        缺点: 1. 库函数,不稳定   2. 调试、编写困难、gdb 不支持  3. 对信号支持不好

        优点相对突出,缺点均不是硬伤。Linux 下由于实现方法导致进程、线程差别不是很大。

5.线程控制原语:

1)获取线程id

线程id是在进程地址空间内部,用来标识线程身份的id号。

pthread_t pthread_self(void);            

        返回值:本线程id

2) 创建子线程。

int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*start_rountn)(void *), void *arg);    

        参1:传出参数,表新创建的子线程 id

        参2:线程属性。传NULL表使用默认属性

        参3:子线程回调函数。创建成功,pthread_create函数返回时,该函数会被自动调用。
        
        参4:参3的参数。没有的话,传NULL

        返回值:成功:0

            失败:errno

获取线程id、创建子线程:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>

void sys_err(const char *str)
{
	perror(str);
	exit(1);
}

//子线程
void *tfn(void *arg)    //子进程回调函数
{
    printf("thread: pid = %d, tid = %lu\n", getpid(), pthread_self());//获取进程id

    return NULL;
}

//主线程
int main(int argc, char *argv[])
{
    pthread_t tid;

    int ret = pthread_create(&tid, NULL, tfn, NULL);    //创建子进程
    if (ret != 0) {
        perror("pthread_create error");
    }

    printf("main: pid = %d, tid = %lu\n", getpid(), pthread_self());//获取进程id
    //sleep(1);

	//return 0;
    pthread_exit((void *)0);
}

 *3)循环创建N个子线程:

        for (i = 0; i < 5; i++)

            pthread_create(&tid, NULL, tfn, (void *)i);   // 将 int 类型 i, 强转成 void *, 传参。    
 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>

void sys_err(const char *str)
{
	perror(str);
	exit(1);
}

void *tfn(void *arg)
{
    int i = (int)arg;           //强转.受参数类型限制
    sleep(i);
    printf("--I'm %dth thread: pid = %d, tid= %lu\n", i+1, getpid(), pthread_self());

    return NULL;
}

int main(int argc, char *argv[])
{
    int i;
    int ret;
    pthread_t tid;

    for (i = 0; i < 5; i++) {
        ret = pthread_create(&tid, NULL, tfn, (void *)i); // i 传参采用 值传递. 借助强转.
        if (ret != 0) {
            sys_err("pthread_create error");
        }
    }

    sleep(i);
    printf("main: I'm Main, pid = %d, tid= %lu\n", getpid(), pthread_self());

	return 0;
}

4)线程中全局变量共享

线程默认共享数据段、代码段等地址空间,常用的是全局变量。而进程不共享全局变量,只能借助 mmap

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

int var = 100;

void *tfn(void *arg)
{
	var = 200;
	printf("thread, var = %d\n", var);

	return NULL;
}

int main(void)
{
	printf("At first var = %d\n", var);

	pthread_t tid;
	pthread_create(&tid, NULL, tfn, NULL);
	sleep(1);

	printf("after pthread_create, var = %d\n", var);

	return 0;
}

 如果遇到以下编译问题,不要慌张

问题的原因pthread不是linux下的默认的库,也就是在链接的时候,无法找到phread库中哥函数的入口地址,于是链接会失败。

解决:在gcc编译的时候,附加要加 -lpthread参数即可解决。

试用如下命令即可编译通过

 gcc z.c -o z -lpthread

5)   退出当前线程

void pthread_exit(void *retval);  

        retval:退出值。 无退出值时,NULL

        exit();    退出当前进程。

        return: 返回到调用者那里去。

        pthread_exit(): 退出当前线程。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>

void sys_err(const char *str)
{
	perror(str);
	exit(1);
}

void *tfn(void *arg)
{
    int i = (int)arg;           //强转.受参数类型限制
    sleep(i);
    if(i == 2){

    }
    printf("--I'm %dth thread: pid = %d, tid= %lu\n", i+1, getpid(), pthread_self());

    return NULL;
}

int main(int argc, char *argv[])
{
    int i;
    int ret;
    pthread_t tid;

    for (i = 0; i < 5; i++) {
        ret = pthread_create(&tid, NULL, tfn, (void *)i); // i 传参采用 值传递. 借助强转.
        if (ret != 0) {
            sys_err("pthread_create error");
        }
    }

    sleep(i);
    printf("main: I'm Main, pid = %d, tid= %lu\n", getpid(), pthread_self());

	return 0;
}

 exit(0)   退出线程

/********使用exit(0)**************/

void *tfn(void *arg)
{
    int i = (int)arg;        
    sleep(i);
    if(i == 2){
        exit(0);    //表示退出线程
    }
    printf("--I'm %dth thread: pid = %d, tid= %lu\n", i+1, getpid(), pthread_self());

    return NULL;
}

 return NULL 返回到调用者那里

/**********使用return NULL**************/

void *tfn(void *arg)
{
    int i = (int)arg;          
    sleep(i);
    if(i == 2){
       return NULL;    //返回到调用者那里
    }
    printf("--I'm %dth thread: pid = %d, tid= %lu\n", i+1, getpid(), pthread_self());

    return NULL;
}

pthread_exit(NULL)  当前线程退出

/*****使用pthread_exit(NULL)  当前线程退出********/

void func(void)
{
    pthread_exit(NULL);

    return;
}

void *tfn(void *arg)
{
    int i = (int)arg;      //强转.受参数类型限制
    sleep(i);
    if(i == 2){
      func();    //当前线程退出
    }
    printf("--I'm %dth thread: pid = %d, tid= %lu\n", i+1, getpid(), pthread_self());

    return NULL;
}




/*********或者*************/


void *tfn(void *arg)
{
    int i = (int)arg;           //强转.受参数类型限制
    sleep(i);
    if(i == 2){
      pthread_exit(NULL);    //当前线程退出
    }
    printf("--I'm %dth thread: pid = %d, tid= %lu\n", i+1, getpid(), pthread_self());

    return NULL;
}

*6)   阻塞 回收线程

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

        thread: 待回收的线程id

        retval:传出参数。 回收的那个线程的退出值。

            线程异常借助,值为 -1。

        返回值:成功:0

            失败:errno

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>

struct thrd{
    int var;
    char str[256];
};

void sys_err(const char *str)
{
    perror(str);
    exit(1);
}

void *tfn(void *arg)
{
    struct thrd *tval;

    tval = malloc(sizeof(tval));

    tval->var = 100;
    strcpy(tval->str,"hello thread");
    
    return (void *)tval;
}

int main(int argc,char *argv[])
{
    pthread_t tid;
    struct thrd *retval;  

    int ret = pthread_create(&tid,NULL,tfn,NULL);
    if(ret != 0)
        sys_err("pthread_create error");

    ret = pthread_join(tid,(void **)&retval);
    if(ret != 0)
        sys_err("pthread_join error");
    
    printf("child thread exit with var = %d,str = %s\n",retval->var,retval->str);

    pthread_exit(NULL);

    return 0;
}

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>

void sys_err(const char *str)
{
    perror(str);
    exit(1);
}

void *tfn(void *arg)
{
    
    return (void *)74;
}

int main(int argc,char *argv[])
{
    pthread_t tid;

    int *retval;
    int ret = pthread_create(&tid,NULL,tfn,NULL);
    if(ret != 0)
        sys_err("pthread_create error");

    ret = pthread_join(tid,(void **)&retval);
    if(ret != 0)
        sys_err("pthread_join error");

    printf("child thread exit with %d\n",(void *)retval);

    pthread_exit(NULL);

    return 0;
}

 注意:

 局部变量地址,不可做返回值

void *tfn(void *arg)
{
    struct thrd tval;    //局部变量地址,不可做返回值

    tval = malloc(sizeof(tval));

    tval->var = 100;
    strcpy(tval->str,"hello thread");
    
    return (void *)tval;
}

   7)杀死一个线程。

 需要到达取消点(保存点)

int pthread_cancel(pthread_t thread);      

        thread: 待杀死的线程id
        
        返回值:成功:0

            失败:errno

        如果,子线程没有到达取消点, 那么 pthread_cancel 无效

        我们可以在程序中,手动添加一个取消点。使用 pthread_testcancel();

        成功被 pthread_cancel() 杀死的线程,返回 -1.使用pthead_join 回收

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>


void tfn()
{
    while(1){
        printf("thread: pid = %d,tid = %lu\n",getpid(),pthread_self());
        sleep(1);
    }
    return NULL;
}
int main(int argc,char *argv[])
{

    pthread_t tid;

    int ret = pthread_create(&tid,NULL,tfn,NULL);
    if(ret != 0){
        fprintf(stderr,"pthread_create error:%s\n",strerror(ret));
        exit(1);
    }

    printf("main: pid =%d,tid = %lu\n",getpid,pthread_self());

    sleep(5);
    
    ret = pthread_cancel(tid);

    if(ret != 0){
        fprintf(stderr,"pthread_cancel error:%s\n",strerror(ret));
        exit(1);
    }
    
    while(1);
    
    return 0;
}

8)设置线程分离

int pthread_detach(pthread_t thread);        

        thread: 待分离的线程id
        

            返回值:成功:0

            失败:errno    

注意:检查出错返回:  线程中:

        fprintf(stderr, "xxx error: %s\n", strerror(ret));

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>


void *tfn(void *arg)
{
    printf("thread: pid = %d, tid= %lu\n",getpid(), pthread_self());

    return NULL;
}

int main(int argc, char *argv[])
{
   
    int ret;
    pthread_t tid;

  
    ret = pthread_create(&tid, NULL, tfn, NULL); // i 传参采用 值传递. 借助强转.
    if (ret != 0) {
        fprintf(stderr,"pthread_create error :%s\n",strerror(ret));
        exit(1);
    }

    ret = pthread_detach(tid);    //设置线程分离,线程终止,会自动清理pcb,无需回收
    if (ret != 0) {
        fprintf(stderr,"pthread_detach error :%s\n",strerror(ret));
        exit(1);
    }

    sleep(2);
    
    ret = pthread_join(tid,NULL);    //tid为无效值
    if (ret != 0) {
        fprintf(stderr,"pthread_join error :%s\n",strerror(ret));
        exit(1);
    }

    printf("main: I'm Main, pid = %d, tid= %lu\n", getpid(), pthread_self());
	
    pthread_exit((void *)0);
}


  

9)   线程与进程原语比较

    线程控制原语                    进程控制原语


    pthread_create()                fork();

    pthread_self()                    getpid();

    pthread_exit()                    exit(); / return 

    pthread_join()                    wait()/waitpid()

    pthread_cancel()                kill()

    pthread_detach()

6.线程属性:

    设置分离属性

    pthread_attr_t attr      创建一个线程属性结构体变量

    pthread_attr_init(&attr);    初始化线程属性

    pthread_attr_setdetachstate(&attr,  PTHREAD_CREATE_DETACHED); 

    设置线程属性为分离态

    pthread_create(&tid, &attr, tfn, NULL); 借助修改后的 设置线程属性 创建为分离态的新线程

    pthread_attr_destroy(&attr);    销毁线程属性

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>


void *tfn(void *arg)
{
    printf("thread: pid = %d, tid = %lu\n", getpid(), pthread_self());

    return NULL;
}

int main(int argc, char *argv[])
{
    pthread_t tid;

    pthread_attr_t attr;

    int ret = pthread_attr_init(&attr);
    if (ret != 0) {
        fprintf(stderr, "attr_init error:%s\n", strerror(ret));
        exit(1);
    }

    ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);      // 设置线程属性为 分离属性
    if (ret != 0) {
        fprintf(stderr, "attr_setdetachstate error:%s\n", strerror(ret));
        exit(1);
    }

    ret = pthread_create(&tid, &attr, tfn, NULL);
    if (ret != 0) {
        perror("pthread_create error");
    }

    ret = pthread_attr_destroy(&attr);
    if (ret != 0) {
        fprintf(stderr, "attr_destroy error:%s\n", strerror(ret));
        exit(1);
    }

    //sleep(1)    //等待子线程死亡

    //因为join是可以阻塞的,所以不sleep也可以

    ret = pthread_join(tid, NULL);    //回收子线程,回收失败线程分离
    if (ret != 0) {
        fprintf(stderr, "pthread_join error:%s\n", strerror(ret));
        exit(1);
    }

    printf("main: pid = %d, tid = %lu\n", getpid(), pthread_self());

    pthread_exit((void *)0);
}

 7.线程使用注意事项

        1). 主线程退出其他线程不退出,主线程应调用 pthread_exit

        2). 避免僵尸线程

                 pthread_join

                 pthread_detach

                 pthread_create 指定分离属性

                被 join 线程可能在 join 函数返回前就释放完自己的所有内存资源,所以不应当返回被回收线程栈中的值;

        3). malloc 和 mmap 申请的内存可以被其他线程释放

        4). 应避免在多线程模型中调用 fork 除非,马上 exec,子进程中只有调用 fork 的线程存在,其他线程在子进程 中均 pthread_exit

         5) . 信号的复杂语义很难和多线程共存,应避免在多线程引入信号机制


    


    

    


 

    

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

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

相关文章

【微信小程序】项目初始化

| var() CSS 函数可以插入一个自定义属性&#xff08;有时也被称为“CSS 变量”&#xff09;的值&#xff0c;用来代替非自定义 属性中值的任何部分。 1.初始化样式与颜色 view,text{box-sizing: border-box; } page{--themColor:#ad905c;--globalColor:#18191b;--focusColor…

通过Vcpkg直接安装Qt静态链接免编译的方式

Vcpkg支持自动编译Qt静态链接版&#xff0c;大约只需要30min左右&#xff0c;解决编译困扰和配置环境带来的各种影响。 一、查找Qt包 https://vcpkg.io/en/packages 输入qt5可找到qt5版本&#xff0c;如果需要安装最新的qt6&#xff0c;可直接输入qt&#xff0c;如下: 二、修…

操作系统存储器章节知识梳理

&#x1f525;&#x1f525;宏夏Coding网站&#xff0c;致力于为编程学习者、互联网求职者提供最需要的内容&#xff01;网站内容包括求职秘籍&#xff0c;葵花宝典&#xff08;学习笔记&#xff09;&#xff0c;资源推荐等内容。在线阅读&#xff1a;https://hongxiac.com&…

关于阻抗不连续的原因

1、连线中的分支结构 信号传输到分支结构处&#xff0c;表现出的是两条电路并联的效果&#xff0c;会导致阻抗的不连续问题。典型的就是菊花链结构。 2、参考平面的宽度 一般情况下参考平面都是很宽且连续的&#xff0c;但有的时候&#xff0c;参考平面会被反焊盘给掏空…

FPGA/数字IC(芯海科技2022)面试题 2(解析版)

以下仅为学习参考(非原创)&#xff0c;如有疑惑欢迎评论区指出&#xff01; 一、单选题&#xff08;共20题&#xff0c;每题3分&#xff0c;共60分&#xff09; 1. D触发器&#xff1a;Tsetup3ns&#xff0c;Thold1ns&#xff0c;Tck2q1ns&#xff0c; 该D触发器最大可运行时…

笔记1.5:计算机网络体系结构

从功能上描述计算机网络结构 分层结构 每层遵循某个网络协议完成本层功能 基本概念 实体&#xff1a;表示任何可发送或接收信息的硬件或软件进程。 协议是控制两个对等实体进行通信的规则的集合&#xff0c;协议是水平的。 任一层实体需要使用下层服务&#xff0c;遵循本层…

Linux 下的 10 个 PDF 软件

本文[1]是我们正在进行的有关 Linux 顶级工具系列的延续&#xff0c;在本系列中&#xff0c;我们将向您介绍最著名的 Linux 系统开源工具。 随着互联网上越来越多地使用可移植文档格式 (PDF) 文件来获取在线书籍和其他相关文档&#xff0c;拥有 PDF 查看器/阅读器对于桌面 Linu…

Wireshark分析HTTPS流量

1、设置环境变量 &#xff08;在不抓包的时候要把这个环境变量删掉,日志太多可能&#xff09; # 变量名称 SSLKEYLOGFILE # 变量值&#xff1a;日志文件存储的路径 D:\data\ssl\keylog.log2、 配置配置Wireshark软件文件路径 配置上之后一定要重启&#xff0c;不重启没效果。

Math_2023_09_17.java

package homework;import util.StringUtil;/*** 八位数: 2 _ _ _ _ _ _ 5* 每相邻三个数字和为11** author ZengWenFeng* email 117791303qq.com* mobile 13805029595* date 2023.09.17*/ public class Math_2023_09_17 { public static boolean isOk(String str, int sum){in…

Apache Hive概述,模拟实现Hive功能,Hive基础架构

1、Apache Hive 概述 1.1、分布式SQL计算 对数据进行统计分析&#xff0c;SQL是目前最为方便的编程工具。 大数据体系中充斥着非常多的统计分析场景 所以&#xff0c;使用SQL去处理数据&#xff0c;在大数据中也是有极大的需求的。 MapReduce支持程序开发&#xff08;Java…

88 # express 应用和路由的分离

上一节实现了应用和创建应用的分离&#xff0c;这一节来实现应用和路由的分离 application.js const http require("http"); const Router require("./router");function Application() {this._router new Router(); }Application.prototype.get fun…

Learn Prompt-ChatGPT 精选案例:内容总结

ChatGPT 可以通过分析内容并生成一个浓缩版本来总结文本。这对节省时间和精力很有帮助&#xff0c;特别是在阅读长篇文章、研究论文或报告时。 通用总结​ 你所要做的就是把具体的文字复制并粘贴到提示中&#xff0c;并要求ChatGPT对所选文本进行简化总结。这里我们参考opena…

[尚硅谷React笔记]——第1章 React简介

目录&#xff1a; 第1章 React简介 React的基本使用:虚拟DOM的两种创建方式&#xff1a; 使用jsx创建虚拟DOM使用js创建虚拟DOM(一般不用)虚拟DOM与真实DOM:React JSX:JSX练习&#xff1a;模块与组件、模块化与组件化的理解 模块组件模块化组件化 第1章 React简介 中文官网: …

【微信小程序】关于页面中引入背景的两种方式

| 布局设计思路 <view class"about"> <view class"pubilcTitle"><view class"en"></view><view class"cn"></view><view class"line"></view> </view> <view cl…

linux基础(2)

目录 一.vi\vim编译器介绍1.三种模式2.vim的使用3.快捷键的使用 二.which&#xff0c;find命令三.grep命令四.wc命令五.管道符六.echo命令1.重定向符 七.tail命令 一.vi\vim编译器介绍 vim\vi是linux中最经典的文本编译器 同图形化界面中的文本编译器是一样的&#xff0c;vi是…

记录:移动设备软件开发(搭建Android开发环建)

目录 前言安装Android Studio 前言 Android是一种基于Linux的开放源代码操作系统&#xff0c;主要用于移动设备&#xff0c;如智能手机和平板电脑。Android提供了一个丰富的应用程序框架&#xff0c;允许开发者使用Java语言创建创新的应用程序。为了开发Android应用程序&#…

前 K 个高频元素

题目链接 前 K 个高频元素 题目描述 注意点 k 的取值范围是 [1, 数组中不相同的元素的个数]题目数据保证答案唯一&#xff0c;换句话说&#xff0c;数组中前 k 个高频元素的集合是唯一的返回其中出现频率前 k 高的元素可以按 任意顺序 返回答案 解答思路 使用哈希表存储所…

Go-Python-Java-C-LeetCode高分解法-第六周合集

前言 本题解Go语言部分基于 LeetCode-Go 其他部分基于本人实践学习 个人题解GitHub连接&#xff1a;LeetCode-Go-Python-Java-C Go-Python-Java-C-LeetCode高分解法-第一周合集 Go-Python-Java-C-LeetCode高分解法-第二周合集 Go-Python-Java-C-LeetCode高分解法-第三周合集…

Learn Prompt-为什么用 ChatGPT API?

引用人工智能先驱吴恩达先生说过的话&#xff1a;“一个系统需要的远不止一个提示&#xff08;prompt&#xff09;或者一个对LLM&#xff08;大性语言模型&#xff09;的调用。” API的优点&#xff1a; 集成更深: 通过 API&#xff0c;您可以将 ChatGPT 集成到自己的系统和工…

腾讯云阿里云云服务器 Linux 操作系统 BT 宝塔面板快速建站教程

宝塔面板概述 宝塔面板是一款服务器管理软件&#xff0c;支持Windows和Linux系统&#xff0c;可以通过Web端轻松管理服务器&#xff0c;提升运维效率。总体来说&#xff0c;宝塔面板具有操作简单、功能丰富、安全可靠等特点&#xff0c;是一款非常实用的服务器管理软件。 宝塔…