【Linux04-进程概念上】两个结构的理解带你“降维打击”进程概念!

news2024/11/25 20:31:43

前言

本期分享Linux超重要的知识:进程概念!

博主水平有限,不足之处望请斧正!


要学习进程,我们首先得搭建宏观的计算机和操作系统结构,从更高的视角学习。

先导

计算机体系结构

使用最多提及最多的计算机结构当属 冯·诺依曼计算机结构,来看看。
在这里插入图片描述

了解结构,可得出结论: CPU只和内存打交道(也会控制输入输出设备和存储器)。

*CPU虽然很快,但没那么灵光,只会被动接收和执行指令。为了能够接受指令,CPU有自己的指令集(精简/复杂指令集)。所谓编译源文件为二进制可执行程序,其实就是按照CPU指令集来编译,形成此CPU可以执行的程序。

现象解释

  • 【程序运行必须要加载到内存,为什么?】

    :程序存在磁盘,CPU执行程序不直接和外设沟通,加载到内存后CPU才能和程序沟通。

  • 【printf运行完怎么不显示?】

    :printf打印的信息由CPU输出到内存,按期刷新,并不是直接输出到输出设备如显示器。

  • 【内存怎么知道输入设备输入了数据?】

    : CPU虽然“只和内存打交道”,但也会控制输入输出设备和存储器。

  • 【从计算机体系结构理解,聊天是啥样的?】
    :键盘输入到内存 => CPU计算 => 放回内存 => 输出到网卡并传输到对方网卡 =>

    对方网卡接收到数据后输入到内存 => CPU计算 => 放回内存 => 输出到显示器

操作系统体系结构

在这里插入图片描述

从图中可以得知,

操作系统是一个进行软硬件资源管理的软件,向下通过驱动管理好硬件,向上通过系统调用提供良好的运行环境。

向下管理软件

【为什么要管理?】

通过合理管理软硬件资源(方式),给用户提供良好(稳定、安全、高效)的运行环境(目的)。

【怎么管理?】

生活中所有事无非就是 决策或执行。个体常常既是决策者也是执行者;但对于一个组织,决策和执行融合效率不高,所以都是决策执行分离。

通过学校的例子来理解:

校长不需要和学生打交道也能管理学生,辅导员收集学生信息(姓名、班级和学号等),校长再通过这些数据来做决策,从而达到管理学生的目的。

在这里插入图片描述

  • 校长就像OS(通过数据做决策,达到管理目的)

  • 辅导员就像驱动( 收集数据和执行决策)

  • 学生就像硬件(被管理)

伪代码:

//抽象
struct stu
{
  struct stu* next; //下一个结点
	char* name;
	char* sex;
	int age;
	//...
}

void insert(...) {...};
void erase(...) {...};

int main()
{
  	//具象:定义结构体变量
  	struct stu ZhangSan = { ... };
		struct stu LiSi = { ... };
		struct stu WangWu = { ... };
  	
  	//一种组织方式:链表
  	ZhangSan->next = Lisi;
  	Lisi->next = Wangwu;
  
		//操作
  	insert(...);
  	erase(...);
  
  	return 0;
}

stu结构体抽象学生 ==> 定义stu结构体变量 ==> 用链表组织 ==> 操作链表。

对学生的管理 ==> 对学生信息和属性的管理 ==> 对结构体变量的管理 ==> 对链表的管理。

可得结论:

管理的本质 = 对数据做管理

管理的方法 = 抽象 + 具象 + 组织 + 操作

计算机中也是如此:

在这里插入图片描述

伪代码:

//抽象
struct dev_struct
{
  struct stu* next; //下一个结点
	char* dev_type;
  char* dev_status;
	//...
}

void insert(...) {...};
void erase(...) {...};

int main()
{
  	//具象:定义结构体变量
  	struct dev_struct disk1 = {XXX, XXX, XXX, ...};
  	struct dev_struct disk2 = {XXX, XXX, XXX, ...};
  	
  	//一种组织方式:链表
		disk1->next = disk2;
  
		//操作
  	insert(...);
  	erase(...);
  
  	return 0;
}

dev_struct结构体抽象 ==> 定义dev_struct结构体变量 ==> 用链表组织 ==> 操作链表。

对硬件的管理 ==> 对硬件信息和属性的管理 ==> 对结构体变量的管理 ==> 对链表的管理。

“管理”,精华与进化

这个“管理”,非常精华。怎么说?

譬如我们写三子棋,本质就是管理棋盘——抽象棋盘、具象棋盘、组织棋盘、操作棋盘;我们写通讯录,本质就是管理通讯录——抽象通讯录和人、具象通讯录和人,把人放进通讯录、组织通讯录和通讯录中的人、管理通讯录和通讯录中的人。

又或者说是高级语言的出现,如C++、Java。我们写程序上来就是写一个类,也能使用已经准备好的容器。为什么?
因为这些高级语言看到了软件的本质:完成某种管理工作。
语言中许多设计都是为了方便我们完成管理工作,比如容器、算法等等。

能明白所有软件的精华都是完成某种管理工作,从而在语言层面提供高效率的“四步走”流程,这何尝不是一种“进化”?

向上提供良好运行环境

通过银行的例子理解:

到银行办业务的时候,都是通过一大块玻璃(强度极高)的一个小口和业务员沟通

在这里插入图片描述

【为什么要“小口”?】

去掉玻璃不行吗?

银行里有大量的“万恶之源”,很容易受到攻击——你存个钱,自己跑进钱库放进去,不现实!

所以银行需要保护自己。

那既然是为了安全,彻底封死不是更安全吗?

银行的意义,就是提供存取款服务,满足用户的需求。

“小口”就是权衡了这两个问题的一个解:接口式服务,你给我资料,我办完业务给你个结果。

既保护了自己,也保障了需求。

【怎么提供?】

给用户提供系统调用(系统接口)——即保护操作系统,也保障需求。

  • 保护操作系统:OS很脆弱,不能随意访问,一点修改就能让它崩掉。
  • 保障需求:尽管OS很脆弱,但它存在的意义就是为了给我们良好运行环境,一个系统调用都不提供,就没办法保障原始的需求。

但,仅仅是打印"hello world!"到显示器,就得调用系统接口,岂不是太麻烦了?

是的,所以有:

  • shell:通过指令操作调用系统接口
  • lib:通过库函数调用系统接口
  • 图形化界面:通过图形化界面和鼠标点击调用系统接口

宏观的结构清晰了,就可以更好地学习进程。

是什么

进程是 加载到内存的程序
在这里插入图片描述

*程序本质:磁盘上的文件。

进程多起来以后,OS要不要把它们管理起来?当然要的,怎么管理呢?

【当内存中进程很多,如何管理?】

同样的四步走,但是,进程要怎么抽象?

#进程的抽象

宏观来讲,

A程序载入内存变为A进程,A进程的所有属性和信息(包括A程序的代码和数据)都被放在一个叫做**PCB(进程控制块)**的结构体变量中,也意味着,PCB是进程的抽象

PCB是操作系统中宏观的概念,到具体的操作系统,

Linux中, task_struct是进程的抽象。

所以,进程也可以说是 内核数据结构task_struct + 某程序在磁盘内的代码 + 数据

前者的各种属性允许操作系统进行管理,调度等操作,后者是CPU执行需要用到的。

task_struct里有些什么呢?

struct task_struct {
	volatile long state;	/* -1 unrunnable, 0 runnable, >0 stopped */
	void *stack;
	atomic_t usage;
	unsigned int flags;	/* per process flags, defined below */
	unsigned int ptrace;

	int lock_depth;		/* BKL lock depth */
	/* ... */
}

所以Linux中,这样管理进程:

task_struct结构体抽象 ==> 定义task_struct结构体变量 ==> 内核数据结构组织 ==> 内核算法操作。

对进程的管理 ==> 对进程信息和属性的管理 ==> 对结构体变量的管理 ==> 对链表的管理。

知道如何抽象进程就可以得出,当sort.exe加载进内存,操作系统帮我们做了这些事来管理:

//具象:创建一个进程控制块
struct task_struct sort = malloc(sizeof(struct task_struct)); 
sort->status = XXX;  	 //填入属性
sort->priority = XXX;  //填入属性

//组织
sort->next = XXX; 	   //一种组织方式:链表

//操作
insert(...);
erase(...);

//...

怎么操作

查看进程

ps ajx:查看所有进程

ps ajx | head -1:显示第一行

[bacon@VM-12-5-centos linux]$ ps ajx | head -1
 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND

*pid是某进程的编号,ppid是某进程的父进程的编号,STAT是进程状态,等会会讲到。

ps ajx | grep [process]:查找进程

在这里插入图片描述

*grep也是个进程,查找process,所以grep “process” 也能看到grep

cd /proc/ :进入以pid命名的进程目录,查看

在这里插入图片描述

可知进程特性:是文件

进入对应目录还能看到:

在这里插入图片描述

“exe”链接至磁盘某个位置的可执行程序。

如果我们把磁盘上的可执行程序删掉,会怎么样?

在这里插入图片描述

正在运行的process还能运行,因为它已经加载到内存。

进程相关的系统调用

getpid(获取进程id)

NAME
       getpid - get the process ID

SYNOPSIS
       #include <unistd.h>

       pid_t getpid(void);

DESCRIPTION
       The getpid() function shall return the process ID of the calling process.

RETURN VALUE
       The getpid() function shall always be successful and no return value is reserved to indicate an error.

getppid(获取父进程id)

NAME
       getppid - get the parent process ID

SYNOPSIS
       #include <unistd.h>

       pid_t getppid(void);

DESCRIPTION
       The getppid() function shall return the parent process ID of the calling process.

RETURN VALUE
       The getppid() function shall always be successful and no return value is reserved to indicate an error.

看一个现象:
在这里插入图片描述

process每次运行的pid都不同,能理解,因为每次都是重新加载进内存,但是ppid为什么都一样?

大部分进程都是作为命令行解释器bash的子进程运行。

在这里插入图片描述

为什么呢?

结合一个例子理解:

p村的张三,是村长的儿子,对隔壁村的如花很中意,叫王婆这个媒婆牵牵线。王婆去了,但是如花已经有心上人了,王婆转告张三。但张三不死心,三番两次让王婆去,王婆不愿意还他摆出官架子。不想得罪村长,也不想无意义地跑来跑去,就想出一个办法:开一家“世只因佳缘”婚姻介绍所,招一堆实习生,张三再让她去她就派实习生去——事情没办好就是实习生的问题,不管我王婆的事!

以子进程的方式运行,不管你子进程干了什么,都不影响我bash,与我无关!

int main()
{
    printf("Process %d  running..., ppid %d\n", getpid(), getppid());

    int* p = NULL;
    *p = 10;

    return 0;
}

在这里插入图片描述

就算子进程发生Segmentation fault段错误,也不影响我bash——bash依然正常执行指令。

kill -9(杀死进程)

在这里插入图片描述

可以运行,可以被杀掉,可知进程特性:具有动态属性

*kill -l查看kill指令的参数

fork(创建子进程)

NAME
       fork - create a new process

SYNOPSIS
       #include <unistd.h>

       pid_t fork(void);

DESCRIPTION
       The fork() function shall create a new process. The new process (child
       process)  shall  be  an  exact  copy  of  the  calling process
  		 //子进程和父进程只有少部分内容和属性不同
int main()
{
    //只有父进程
    fork();
    //有父进程 + 子进程
    
    printf("process %d running, ppid %d\n", getpid(), getppid());

    return 0;
}
[bacon@VM-12-5-centos 10]$ ./process 
process 24840 running, ppid 19523  #父进程
process 24841 running, ppid 24840  #子进程

创建子进程后,执行流分流,对于后续代码,父子进程各自都要执行。

若我想父进程和子进程执行自己的代码,而不是执行同样的代码呢?

利用返回值。

  • 创建成功的返回值:
    • 给父进程返回子进程pid
    • 给子进程返回0
  • 创建失败的返回值:
    • 给父进程返回-1
    • 子进程不会被创建
int main()
{
    pid_t id = fork();
    
    if(id < 0)
    {
        perror("fork");
    }
    else if(id > 0)
    {
        printf("process %d running, ppid %d, id %d\n", getpid(), getppid(), id);
    }
    else 
    {
        printf("process %d running, ppid %d, id %d\n", getpid(), getppid(), id);
    }

    return 0;
}

[bacon@VM-12-5-centos 10]$ ./process 
process 25997 running, ppid 19523, id 25998
process 25998 running, ppid 25997, id 0

效果是达到了,但是看着怎么不对劲?

同一个变量id有两个不同的值?

不同分支同时执行?

这两 个问题先按下不表。


特性

  • 进程是文件
  • 进程具有动态属性
  • 大部分进程作为bash的子进程运行


#进程状态

是什么

task_struct中的一个属性(整数),用来描述进程当前状态。

/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};

*上为Linux描述进程状态的结构体

为什么

不同的运行场景,需要不同的进程状态表示。

有哪些

读者朋友们可能看到过这些状态:运行、新建、就绪、挂起、阻塞、等待、停止、死亡等等。

接下来谈谈,

先理解宏观的进程状态,再落到具体的操作系统层面“降维理解”Linux的进程状态。

宏观的进程状态

进程状态用语言描述没法真正理解,我们同样地来一张图,了解三种宏观的进程状态:运行、阻塞和挂起。

在这里插入图片描述

蓝色的走线,代表程序从磁盘加载到内存,变成进程,最后运行的过程。即将运行的进程,都先入run_queue,而后由CPU调度运行。为什么有runqueue?CPU很快,如果仅将正在执行的进程认作“运行”状态,那这个状态转瞬即逝,作用就没那么大。

绿色的走线,代表进程访问外设的过程。当需要访问外设(如磁盘),找到对应的结构体对象(disk1),调用其读写方法进行访问。CPU的速度很快,进程可以很快轮转;但外设很慢,进程多了就只能排队。而且CPU不可能陪着外设等,所以外设结构体对象都有自己的wait_queue,CPU会把需要访问外设的进程都扔到其wait_queue,然后接着回去执行其他进程。但wait_queue总也是有限的,当等待着要访问的进程都放不下,就需要只保留基本结构(仍被组织着),将数据和代码换出到磁盘,等有位置了再从磁盘换入。

其中,

  • 在CPU的运行队列 run_queue 中的进程都是 运行状态
  • 在外设结构体对象的等待队列 wait_queue 中的进程都是 阻塞状态
  • 因wait_queue满,无法入队,数据和代码被换出到磁盘的进程都是 挂起状态

进程状态的不同,其实就是进程所处的队列不同。

以上是宏观操作系统理论中的进程状态,具体操作系统的进程状态都以它们为基础。

接下来看看Linux的进程状态。

具体的进程状态(Linux)
  • R(runing) 运行:入了run_queue的进程

  • S(sleeping) 休眠:阻塞的一种

  • T(stop) 停止:可以用kill让它停止

  • t(tracing stop) 停止(被追踪):如调试

  • D(disk sleep) 深度睡眠:D状态相当于免死金牌,几乎无法被终止

  • Z(zombie) 僵尸:进程运行完,等待父进程获取结束信息的状态

  • X(dead) 死亡:进程彻底完事(一般观察不到,这个状态可能都不是设置给用户看的)

  • 后缀"+":表示前台运行(若没有,代表进程后台运行)

【为什么要有Z状态?】

有时候,父进程需要知道子进程任务完成得如何,需要知道子进程退出信息,所以子进程死亡不能立即全部回收资源,得等父进程来获取结束信息

接下来实际看看这些状态

*会用到一个监控脚本,直接在bash运行

while :; do ps ajx | head -1 && ps ajx | grep myproc; sleep 1; done

R

在这里插入图片描述

让进程死循环运行就可以观察到R+前台运行状态。

S
在这里插入图片描述

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

int main()
{
    while(1)
    {
        printf("running...\n");
        sleep(1);
    }
    
    return 0;
}

T

在这里插入图片描述

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>

int main()
{
    while(1)
    {
        printf("%d running...\n", getpid());
        sleep(1);
    }
    
    return 0;
}

kill -l查看kill所有信号。

t

在这里插入图片描述

D

目前没法观察。

Z

在这里插入图片描述

defunct是“死掉的”的意思,标识这个进程已经死了,成为僵尸进程。不回收则会造成内存泄漏。

X

目前没法观察。

#孤儿进程

如果父进程先于子进程退出,子进程就变成孤儿进程,会被操作系统1号进程领养,最终由1号init进程回收。
在这里插入图片描述

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


int main()
{
    pid_t id = fork();
    
    if(id < 0)
    {
        perror("fork");
        return 1;
    }
    else if(id == 0)
    {
        while(1)
        {
            printf("child process, pid:%d, ppid:%d\n", getpid(), getppid());
            sleep(1);
        }
    }
    else 
    {
        int cnt = 5;
        while(cnt)
        {
            printf("parent process, pid:%d, ppid:%d\n", getpid(), getppid());
            --cnt;
            sleep(1);
        }
        printf("parent process exit!\n");
    }
    


    return 0;
}

#进程优先级(priority)

是什么

决定进程运行先后顺序的属性。也就是task_struct中的一个整数。

优先级用 “nice值” 做区分,优先级 = 默认优先级 + nice值。(优先级的值越小,优先级越高,越先执行)

为什么

为什么有优先级?

资源有限,得排队,重要的可以往前排。

怎么操作

怎么查看

ps -elf:查看当前进程的状态

在这里插入图片描述

  • UID:执行者身份
  • PRI:优先级,priority的缩写
  • NI:nice值,nice的缩写

怎么修改

通过nice值来修改

nice∈[-20, 19], 默认80(Linux用两位整数来表示优先级,nice值若不限制,可能影响调度器的均衡性)

[bacon@VM-12-5-centos 5]$ sudo top #修改优先级需要root权限
#输入r:renice
PID to renice [default pid = 1] 26848
Renice PID 26848 to value -20
#修改完成...

[bacon@VM-12-5-centos 5]$ ps -elf | head -1 && ps -elf | grep process
F S UID        PID  PPID  C PRI  NI ADDR SZ WCHAN  STIME TTY          TIME CMD
0 S bacon    26848 11501  0  60 -20 -  1054 hrtime 22:47 pts/1    00:00:00 ./process

sudo top ==> r ==> pid ==> nice value

#进程相关概念

  • 竞争性:进程总是比CPU多的,为了高效完成任务,更合理竞争相关资源,就有了优先级
  • 独立性:进程之间互相独立,互不影响
  • 并行:多个进程在多个CPU下分别同时运行
  • 并发:多个进程在单个CPU下运行(采用进程切换的方式)

独立性的验证:

int main()
{
    pid_t id = fork();
    
    if(id < 0)
    {
        perror("fork");
        return 1;
    }
    else if(id == 0)
    {
        int* p = NULL;
        *p = 100;
     }
    else 
    {
        while(1)
        {
            printf("parent process, pid:%d, ppid:%d\n", getpid(), getppid());
            sleep(1);
        }
    }
    
    return 0;
}

[bacon@VM-12-5-centos 11]$ ./process 
parent process, pid:7701, ppid:25597
parent process, pid:7701, ppid:25597
parent process, pid:7701, ppid:25597
parent process, pid:7701, ppid:25597
^C

子进程解引用野指针,发生段错误,不影响父进程

进程切换:CPU给不同进程分配时间片(A进程运行一会,B进程运行一会,不断轮转),就实现了不同进程在一个CPU上同时推进。

如何轮转的?

我们知道进程运行时会占用CPU,但不是一直占有的。比如写了个死循环给CPU,它也能执行别的进程。既然如此,一定会存在这样的情况:某个进程没跑完就被拿下去了。那下次回来的时候……

通过大学当兵的例子来理解:

大学时,可以保留学籍去当兵。 但张三没给学校打好招呼,而是直接跑去部队。当完兵回学校发现被勒令退学了。学校的视角,张三这个娃课也不上,试也不考,退学!

进程们“离校”,进程切换,得和CPU打好招呼,保护上下文信息——上下文保护

进程们“返校”,恢复运行,得和CPU打好招呼,恢复上下文信息——上下文恢复


本期的分享就到这里,感谢浏览

这里是培根的blog,期待和你共同进步

下期见~

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

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

相关文章

【软件测试】从事5年资深测试的经验,少走弯路......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 小张&#xff1a; 工…

SSIS中刷新Power BI数据集

前面介绍过了怎么在Azure云中刷新Power BI数据集&#xff0c;感兴趣的可以阅读 数据工厂中刷新PowerBI数据集 数据工厂刷新PowerBI数据集2 但有很多公司可能并没有完全上云&#xff0c;比如某些公司还在使用SSIS调用ETL工程&#xff0c;那么有没有办法在本地也实现执行完SSI…

双11购物的凑单问题与财务凑数问题

&#x1f4e2;作者&#xff1a; 小小明-代码实体 &#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/as604049322 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 欢迎讨论&#xff01; &#x1f4e2;本文链接&#xff1a;https://xxmdmst.blog.csdn.n…

Chapter3 P-N Junction

3-2 热平衡状态 电流只在一个方向很容易通过&#xff0c;正方向很容易通过电流&#xff0c;负方向很不容易 正电压加在Ptype上才会有电流 就会产生如图b的现象 electron 一定要从high concentration移动到low concentration 所以两个的移动方向如图所示 靠近junction附近&…

C#中window窗体和控件

C#中window窗体和控件 布局与事件–界面的基本问题 布局 就是拉动窗体的时候&#xff0c;按钮也在跟着变动。 事件 //简单的计算器 using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; namespa…

Web API事件高级部分

1、注册事件&#xff08;2种方式&#xff09; 给元素添加事件&#xff0c;称为 注册事件 或者 绑定事件 注册事件有两种方式&#xff1a;传统方式 和 监听注册方式 传统注册方式&#xff1a; 利用on开头的事件 onclick<button οnclick"alert(hi~)"></b…

供应化学试剂mPEG-Biotin,甲氧基-聚乙二醇-生物素

一&#xff1a;产品描述 1、名称 英文&#xff1a;mPEG-Biotin 中文&#xff1a;甲氧基-聚乙二醇-生物素 2、CAS编号&#xff1a;N/A 3、所属分类&#xff1a;Biotin PEG Methoxy PEG 4、分子量&#xff1a;可定制1k、2k、3.4k、10k、20k、5k 5、质量控制&#xff1a;95…

shell脚本四剑客--sed的应用

sed的介绍 sed是Linux下一款功能强大的非交互流式文本编辑器&#xff0c;可以对文本文件进行增、删、改、查等操作&#xff0c;支持按行、按字段、按正则匹配文本内容&#xff0c;灵活方便&#xff0c;特别适合于大文件的编辑 sed在处理文本时是逐行读取文件内容&#xff0c;…

TCP协议中的几个核心特性

目录 引言 TCP协议 &#x1f351;TCP 与 UDP 的 区别 &#x1f351;TCP客户端和服务器建立连接的三次握手 &#x1f351;TCP客户端和服务器断开连接的四次挥手 &#x1f351;滑动窗口 &#x1f351;流量控制 &#x1f351;拥塞控制 引言 还记得那个经典的图吗&#x…

TCP最大连接数调优

文章目录1、单机最大TCP连接数1.1、如何标识一个TCP连接1.2、client最大tcp连接数1.3、server最大tcp连接数1.3.1、理论连接数1.3.2、实际连接数1.4、单台服务器支撑的最大TCP并发连接数1.4.1、进程限制句柄限制查看进程句柄数限制临时修改重启后失效的修改&#xff08;不过我在…

IIC 通信协议 (二)

目录 引言 子模块设计 思路 单字节 IIC 发送模块 思路 Verilog 源码 多字节发送控制模块 思路 Verilog 源码 仿真 思路 test bench 仿真结果 参考声明 引言 本篇博文承接前文&#xff0c;继续做 IIC 通信协议 FPGA实现相关的内容。用Verilog 编写一个 IIC 通信控…

【折腾服务器 1】妖板 Intel N5105 + i226 主板安装 ESXi 7.0 教程

Catch Up 今年年初&#xff0c;开始搭建个人服务器&#xff0c;用的是一台 Dell 7010 SFF 主机&#xff0c;在上面部署了一些应用&#xff0c;例如&#xff1a;Calibre &#xff0c;Blogs &#xff0c;Minecraft Server 等。使用的是 frp 做的网络代理&#xff0c;有一台服务器…

cubeIDE开发, UART的CubeMX及HAL库实现原理及底层分析

一、UART通信协议 UART通用异步收发器(Universal Asynchronous Receiver and Transmitter)是STM32 上常用的串行通信外设&#xff0c;可以灵活地与外部设备进行全双工数据交换&#xff0c;需要注意区别&#xff1a; 【1】USART-通用同步异步收发器(Universal Synchronous Async…

<Linux线程互斥与死锁>——《Linux》

目录 1. Linux线程互斥 进程线程间的互斥相关背景概念 互斥量mutex 互斥量的接口 初始化互斥量 销毁互斥量 互斥量加锁和解锁 互斥量实现原理探究 可重入VS线程安全 概念 常见的线程不安全的情况 常见的线程安全的情况 常见不可重入的情况 常见可重入的情况 可重…

K. Lonely Numbers(线性筛 + 差分)

Problem - 1423K - Codeforces 在数字世界中&#xff0c;如果两个不同的数字有很多共同点&#xff0c;而且每个数字都有独特的好处&#xff0c;那么它们就是朋友。 更确切地说&#xff0c;如果gcd(a,b), agcd(a,b), bgcd(a,b)能组成一个三角形的边&#xff0c;那么两个不同的数…

六、应用层(四)电子邮件

目录 4.1 电子邮件系统的组成结构 4.2 简单邮件传输协议&#xff08;SMTP&#xff09; 4.3 电子邮件格式 4.4 多用途网际邮件扩充&#xff08;MIME&#xff09; 4.5 邮局协议&#xff08;POP3&#xff09;和因特网报文存取协议&#xff08;IMAP&#xff09; 4.6 基…

小黑下午第一场面试被鸽,一切遇见随缘,继续第二场的leetcode之旅:654. 最大二叉树

小黑代码 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # self.val val # self.left left # self.right right class Solution:def constructMaximumBinaryTree(self, nums: Li…

SAP UI5 应用里 FlexBox 控件的设计原理

sap.m.FlexBox 控件为 flexible box layout 构建容器。VBox 控件为垂直的框布局(vertical flexible box layout)构建容器。 VBox 是一种使用的控件&#xff0c;因为它只是一个定制化的 FlexBox 控件。 VBox 包含 items 聚合&#xff0c;从 FlexBox 继承而来。 HBox 控件为水平…

字符串函数

注意&#xff1a;MySQL中&#xff0c;字符串的位置是从1开始的。 ASCII(S) 返回字符串S中的第一个字符的ASCII码值. 与第一个字符后面的其他字符无关。 SELECTASCII(Abcdfsf) FROM DUAL;CHAR_LENGTH(s) 返回字符串s的字符数。作用与CHARACTER_LENGTH(s)相同。 SELECTCHAR_LEN…

生信基础知识

1.生物数据库分类 &#xff08;1&#xff09;核酸数据库&#xff08;2&#xff09;蛋白质数据库&#xff08;3&#xff09;专用数据库 核酸数据库分为一级核酸数据库和二级核酸数据库 蛋白质数据库分为一级蛋白质数据库和二级蛋白质数据库 一级蛋白质数据库又分为蛋白质序列…