前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Linux下程序是怎样执行的

Linux下程序是怎样执行的

原创
作者头像
高性能架构探索
修改于 2021-04-13 09:44:31
修改于 2021-04-13 09:44:31
5.1K00
代码可运行
举报
文章被收录于专栏:技术随笔心得技术随笔心得
运行总次数:0
代码可运行

如何启动程序

  • 双击(windows系统下),或者在shell终端上执行./a.out
  • 在shell终端上运行可执行程序的标准流程:
    • 启动终端仿真器应用程序
    • 输入可执行文件所在的相对路径或者绝对路径
    • 如果该可执行程序需要输入参数的话,还需要输入参数

比如,我们在终端上输入

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ls --version

就会出现如下结果。ps 在此处,我们可以人为ls为可执行程序的名称,--version 是该程序需要的参数。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ls (GNU coreutils) 8.4
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written by Richard M. Stallman and David MacKenzie.

进入bash: /dev/tty

完整性检查

首先,我们从shell的主函数开始,该函数在shell.c文件中。在主函数执行之前,主要做了以下准备工作:

  • 检查并尝试打开/dev/tty
  • 检查shell是否在调试模式下运行
  • 分析命令行参数
  • 读取shell环境
  • 加载.bashrc、.profile和其他配置文件等。
构建运行环境

在做完上述完整性检查之后,最终会执行reader_loop函数,该函数,定义在eval.c中,主要作用是读取给定的程序名称和参数。然后从execute_cmd.c调用execute_command函数,依次调用以下函数链, 不同的检查,例如我们是否需要启动subshell,是否内置bash函数等等。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
reader_loop
-> execute_command
--> execute_command_internal
----> execute_simple_command
------> execute_disk_command
--------> shell_execve

众所周知,Linux的实现语言是c,shell也是其一个应用,也有自己的main函数。 进入main函数后,在基本的初始化操作之后,最终进入reader_loop函数。 reader_loop会调用execute_command来等待用户输入命令行参数,在用户输入参数之后,将调用execute_command_internal函数。 execute_command_internal函数是shell源码中执行命令的实际操作函数。他需要对作为操作参数传入的具体命令结构的value成员进行分析,并针对不同的value类型,再调用具体类型的命令执行函数进行具体命令的解释执行工作。

具体来说:如果value是simple,则直接调用execute_simple_command函数进行执行,execute_simple_command再根据命令是内部命令或磁盘外部命令分别调用execute_builtin和execute_disk_command来执行,其中,execute_disk_command在执行外部命令的时候调用make_child函数fork子进程执行外部命令。

如果value是其他类型,则调用对应类型的函数进行分支控制。举例来说,如果是value是for_commmand,即这是一个for循环控制结构命令,则调用execute_for_command函数。在该函数中,将枚举每一个操作域中的元素,对其再次调用execute_command函数进行分析。即execute_for_command这一类函数实现的是一个命令的展开以及流程控制以及递归调用execute_command的功能。 在上述整个调用流程串的最后一步是shell_execve。 该函数最终会调用系统函数execve,其声明如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int execve(const char *filename, char *const argv [], char *const envp[]);

在该函数中,有三个参数,分别是:

  • filename可执行文件的名称
  • 可执行文件所需的参数
  • 可执行文件所在的环境变量 在该函数中,最终就是运行可执行程序,这一步操作,是在kernel中操作的。

进入内核: execve系统调用

execve系统调用实现

该函数定义在fs/exec.c中,其声明如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
SYSCALL_DEFINE3(execve,
const char __user *, filename,
const char __user *const __user *, argv,
const char __user *const __user *, envp)
{
      return do_execve(getname(filename), argv, envp);
}

execve的实现在这里非常简单,只调用了do_execve函数,其参数为execve的参数。 而do_execve函数的定义如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int do_execve(struct filename *filename,
const char __user *const __user *__argv,
const char __user *const __user *__envp)
{
    struct user_arg_ptr argv = { .ptr.native = __argv };
    struct user_arg_ptr envp = { .ptr.native = __envp };
    return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
}

通过上述代码,我们可以看到,在do_execve中,最终调用了do_execveat_common,其除了使用do_execve中的参数之外,还有额外的两个参数。 下面是do_execveat_common的具体代码(此处我们去掉了一些不必要放入判断代码)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static int do_execveat_common(int fd, struct filename *filename,
         struct user_arg_ptr argv,
         struct user_arg_ptr envp,
         int flags)
{
 struct linux_binprm *bprm;
 int retval;

 if (IS_ERR(filename))
  return PTR_ERR(filename);

 ...
 current->flags &= ~PF_NPROC_EXCEEDED;

 bprm = alloc_bprm(fd, filename);
 if (IS_ERR(bprm)) {
  retval = PTR_ERR(bprm);
  goto out_ret;
 }

 retval = count(argv, MAX_ARG_STRINGS);
 bprm->argc = retval;

 retval = count(envp, MAX_ARG_STRINGS);
 bprm->envc = retval;

 retval = bprm_stack_limits(bprm);
 
 retval = copy_string_kernel(bprm->filename, bprm);
 bprm->exec = bprm->p;

 retval = copy_strings(bprm->envc, envp, bprm);
 
 retval = copy_strings(bprm->argc, argv, bprm);
 
 retval = bprm_execve(bprm, fd, filename, flags);
 putname(filename);
 return retval;
}
安全性检查

第一个参数AT_FDCWD是当前目录的文件描述符,第五个参数是标志。 我们稍后会看到。 do_execveat_common函数检查文件名指针并返回它是否为NULL。 在此之后,它检查当前进程的标志,表明未超出正在运行的进程的限制:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (IS_ERR(filename))
    return PTR_ERR(filename);

if ((current->flags & PF_NPROC_EXCEEDED) &&
atomic_read(&current_user()->processes) > rlimit(RLIMIT_NPROC)) {
    retval = -EAGAIN;
    goto out_ret;
}

current->flags &= ~PF_NPROC_EXCEEDED;

如果这两项检查成功,我们将在当前进程的标志中取消设置PF_NPROC_EXCEEDED标志,以防止执行程序失败。 在下一步中,我们调用在kernel/fork.c中定义的unshare_files函数,并取消共享当前任务的文件,并检查此函数的结果:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
retval = unshare_files(&displaced);
if (retval)
    goto out_ret;

调用此函数的目的旨在消除执行二进制文件的文件描述符的潜在泄漏。 在下一步中,我们开始准备由struct linux_binprm结构(在include/linux/binfmts.h头文件中定义)表示的bprm。

二进制参数准备

struct linux_binprm

linux_binprm结构用于保存加载二进制文件时使用的参数。 例如,它包含vm_area_struct,表示将在给定地址空间中连续间隔内的单个内存区域,将在该空间中加载应用程序。mm字段,它是二进制文件的内存描述符,指向内存顶部的指针以及许多其他不同的字段。

分配内存

在do_execveat_common函数中,执行alloc_bprm函数,最终会调用如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
if (!bprm)
    goto out_files;
准备工作
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
retval = prepare_bprm_creds(bprm);
if (retval)
    goto out_free;

check_unsafe_exec(bprm);
current->in_execve = 1;

初始化linux_binprm中的cred结构变量,该结构变量中包含任务的实际uid,任务的实际guid,虚拟文件系统操作的uid和guid等。 然后,对check_unsafe_exec函数的调用将当前进程设置为in_execve状态。

计算命令行参数和环境变量
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bprm->argc = count(argv, MAX_ARG_STRINGS);
if ((retval = bprm->argc) < 0)
    goto out;

bprm->envc = count(envp, MAX_ARG_STRINGS);
if ((retval = bprm->envc) < 0)
    goto out;

在上述代码中,MAX_ARG_STRINGS是头文件中定义的上限宏,它表示传递给execve系统调用的最大字符串数。 MAX_ARG_STRINGS的值:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
`#define MAX_ARG_STRINGS 0x7FFFFFFF`
设置

完成所有这些操作后,我们调用do_open_execat函数,该函数

  • 搜索并打开磁盘上的可执行文件并检查,
  • 从noexec挂载点绕过标志0加载二进制文件(我们需要避免从不包含proc或sysfs等可执行二进制文件的文件系统中执行二进制文件),
  • 初始化文件结构并返回此结构上的指针。 接下来,我们可以在此之后看到对sched_exec的调用。 sched_exec函数用于确定可以执行新程序的最小负载处理器,并将当前进程迁移到该处理器。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
file = do_open_execat(fd, filename, flags);
retval = PTR_ERR(file);
if (IS_ERR(file))
    goto out_unmark;

sched_exec();

之后,我们需要检查给出可执行二进制文件的文件描述符。 我们尝试检查二进制文件的名称是否从/符号开始,或者给定的可执行二进制文件的路径是否相对于调用进程的当前工作目录进行了解释,或者文件描述符为AT_FDCWD。 如果这些检查之一成功,我们将设置二进制参数文件名:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bprm->file = file;

if (fd == AT_FDCWD || filename->name[0] == '/') {
    bprm->filename = filename->name;
}

否则,如果文件名称为空,则将文件名设置为/dev/fd/%d (即/dev/fd/文件描述符),否则将文件名重新设置为/dev/fd/%d/文件名(其中,fd指向可执行文件的文件描述符)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
} else {
    
    if (filename->name[0] == '\0')
        pathbuf = kasprintf(GFP_TEMPORARY, "/dev/fd/%d", fd);
    else
        pathbuf = kasprintf(GFP_TEMPORARY, "/dev/fd/%d/%s",    fd, filename->name);
        
    if (!pathbuf) {
        retval = -ENOMEM;
        goto out_unmark;
    }
    
    bprm->filename = pathbuf;
}

bprm->interp = bprm->filename;

需要注意的是,我们不仅设置了bprm-> filename,还设置了bprm-> interp,它将包含程序解释器的名称。 现在,我们只是在此处写相同的名称,但是稍后将使用程序解释器的真实名称对其进行更新,其具体取决于程序的二进制格式。

准备内存相关信息
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
retval = bprm_mm_init(bprm);
if (retval)
     goto out_unmark;

其中,bprm_mm_init的定义如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static int bprm_mm_init(struct linux_binprm *bprm)
{
 int err;
 struct mm_struct *mm = NULL;

 bprm->mm = mm = mm_alloc();
 err = -ENOMEM;
 if (!mm)
  goto err;

 /* Save current stack limit for all calculations made during exec. */
 task_lock(current->group_leader);
 bprm->rlim_stack = current->signal->rlim[RLIMIT_STACK];
 task_unlock(current->group_leader);

 err = __bprm_mm_init(bprm);
 if (err)
  goto err;

 return 0;

err:
 if (mm) {
  bprm->mm = NULL;
  mmdrop(mm);
 }

 return err;
}

在函数bprm_mm_init中,其功能主要是初始化mm_struct 和 vm_area_struct结构。

读取二进制(ELF)文件

调用prepare_binprm函数将inode的uid填充到linux_binprm结构中,并从二进制可执行文件中读取128个字节。 我们只从可执行文件中读取前128个,因为我们需要检查可执行文件的类型。 我们将在后续步骤中阅读可执行文件的其余部分。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
retval = prepare_binprm(bprm);
if (retval < 0)
    goto out;

准备好linux_bprm结构后,我们通过调用copy_strings_kernel函数将可执行二进制文件的文件名,命令行参数和环境变量从内核复制到linux_bprm:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
retval = copy_strings_kernel(1, &bprm->filename, bprm);
if (retval < 0)
    goto out;

retval = copy_strings(bprm->envc, envp, bprm);
if (retval < 0)
    goto out;

retval = copy_strings(bprm->argc, argv, bprm);
if (retval < 0)
    goto out;

并将指针设置为我们在bprm_mm_init函数中设置的新程序堆栈的顶部bprm-> exec = bprm-> p; 堆栈的顶部将包含程序文件名,我们将该文件名存储到linux_bprm结构的exec字段中。

处理参数结构

通过调用exec_binprm函数来存储当前当前任务所在进程的pid

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
retval = exec_binprm(bprm);
if (retval < 0)
    goto out;

在exec_binprm函数中,也会调用search_binary_handler。 当前,Linux内核支持以下二进制格式:

  • binfmt_script: 支持从#!开始的解释脚本。 线;
  • binfmt_misc: 根据Linux内核的运行时配置,支持不同的二进制格式;
  • binfmt_elf: 支持elf格式;
  • binfmt_aout: 支持a.out格式;
  • binfmt_flat: 支持平面格式;
  • binfmt_elf_fdpic: 支持elf FDPIC二进制文件;
  • binfmt_em86: 支持在Alpha机器上运行的Intel elf二进制文件。 因此,search_binary_handler尝试调用load_binary函数并将linux_binprm传递给该函数。 如果二进制处理程序支持给定的可执行文件格式,它将开始准备可执行二进制文件的前期工作。该函数定义如下:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int search_binary_handler(struct linux_binprm *bprm)
{
    ...
    ...
    ...
    list_for_each_entry(fmt, &formats, lh) {
    retval = fmt->load_binary(bprm);
    
    if (retval < 0 && !bprm->mm) {
        force_sigsegv(SIGSEGV, current);
        return retval;
    }
}

return retval;

在load_binary中检查linux_bprm缓冲区中的魔数(每个elf二进制文件的头中都包含魔数,我们从可执行二进制文件中读取了前128个字节),如果不是elf二进制,则退出。

运行

完整性检测

如果给定的可执行文件为elf格式,则load_elf_binary继续并检查可执行文件的体系结构和类型,并在体系结构错误且可执行文件不可执行,不可共享时退出:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (loc->elf_ex.e_type != ET_EXEC && loc->elf_ex.e_type != ET_DYN)
    goto out;
if (!elf_check_arch(&loc->elf_ex))
    goto out;
设置地址空间和依赖

尝试加载描述段的程序头表。 从磁盘上读取与我们的可执行二进制文件链接的程序解释器和库,并将其加载到内存中。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
elf_phdata = load_elf_phdrs(&loc->elf_ex, bprm->file);
if (!elf_phdata)
    goto out;

程序解释器指定在可执行文件的.interp部分(在大多数情况下,对于x86_64,链接器为– /lib64/ld-linux-x86-64.so.2)。 它设置堆栈并将elf二进制文件映射到内存中的正确位置,映射了bss和brk部分,并做了许多其他不同的事情来准备要执行的可执行文件。 在执行load_elf_binary的最后,我们调用start_thread函数并将三个参数传递给该函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    start_thread(regs, elf_entry, bprm->p);
    retval = 0;
out:
    kfree(loc);
out_ret:
    return retval;

这些参数是:

  • 新任务的寄存器集
  • 新任务入口点的地址
  • 新任务的堆栈顶部地址 从函数名称可以理解,它启动了一个新线程,但事实并非如此。 start_thread函数只是准备新任务的寄存器以准备运行。下面是定义:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void
start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
{
    start_thread_common(regs, new_ip, new_sp,
    __USER_CS, __USER_DS, 0);
}

通过上面代码,我们能够看到,在start_thread函数中,最终还是调用了start_thread_common函数。

开始执行
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static void
start_thread_common(struct pt_regs *regs, unsigned long new_ip,
unsigned long new_sp,
unsigned int _cs, unsigned int _ss, unsigned int _ds)
{
    loadsegment(fs, 0);
    loadsegment(es, _ds);
    loadsegment(ds, _ds);
    load_gs_index(0);
    regs->ip = new_ip;
    regs->sp = new_sp;
    regs->cs = _cs;
    regs->ss = _ss;
    regs->flags = X86_EFLAGS_IF;
    force_iret();
}

start_thread_common函数将fs段寄存器填充为零,并将es&ds填充数据段寄存器的值。之后,我们将新值设置为指令指针,cs段等。在start_thread_common函数的末尾,我们可以看到force_iret宏,该宏通过iret指令强制返回系统调用。

然后,创建了在用户空间中运行的新线程,随后可以从exec_binprm返回,再次处于do_execveat_common中。 exec_binprm完成执行后,释放之前分配的结构的内存,然后返回。

从execve系统调用处理程序返回后,将开始执行程序。之所以可以这样做,是因为之前配置了所有与上下文相关的信息。

如我们所见,execve系统调用不会将控制权返回给进程,但是调用者进程的代码,数据和其他段只是被程序段所覆盖。 应用程序的退出将通过退出系统调用实现。

至此,整个程序从开始运行到退出,整个流程完。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
独家 | 使用Python的OpenAI Gym对Deep Q-Learning的实操介绍(附学习资源)
即使你不喜欢玩游戏,深度强化学习也很重要。只用看当前使用深度强化学习进行研究的各种功能就知道了:
数据派THU
2019/05/21
1.4K0
DeepMind到底是如何教AI玩游戏的?这篇在Medium上获得1700个赞的文章,把里面的原理讲清楚了
一篇顶十篇!想入门强化学习,专心研读这篇对DeepMind经典论文的解析就够了 作者 | Aman Agarwal 编译 | Shawn 编辑 | 鸽子、焦燕 DeepMind到底是如何教AI玩游戏的?这篇在Medium上获得1700个赞的文章,把里面的原理讲清楚了。 谷歌的DeepMind是世界一流的AI研究团队,其研发的AlphaGo在2016年备受瞩目的人机大战中击败了韩国围棋冠军李世石(Lee Sedol),一战成名。AlphaGo背后的关键技术就是深度强化学习(Deep Reinforcem
AI科技大本营
2018/04/26
1.6K0
DeepMind到底是如何教AI玩游戏的?这篇在Medium上获得1700个赞的文章,把里面的原理讲清楚了
塔秘 | DeepMind到底是如何教AI玩游戏的?
导读 DeepMind到底是如何教AI玩游戏的?这篇在Medium上获得1700个赞的文章,把里面的原理讲清楚了。 谷歌的DeepMind是世界一流的AI研究团队,其研发的AlphaGo在2016年备
灯塔大数据
2018/04/04
2.5K0
塔秘 | DeepMind到底是如何教AI玩游戏的?
小白系列(6)| Q-Learning vs. Deep Q-Learning vs. Deep Q-Network
原文:小白系列(6)| Q-Learning vs. Deep Q-Learning vs. Deep Q-Network
一点人工一点智能
2023/09/07
7140
小白系列(6)| Q-Learning vs. Deep Q-Learning vs. Deep Q-Network
Hands on Reinforcement Learning 07 Deep Q Network
在第 5 章讲解的 Q-learning 算法中,我们以矩阵的方式建立了一张存储每个状态下所有动作值的表格。表格中的每一个动作价值
一只野生彩色铅笔
2023/04/07
7230
Hands on Reinforcement Learning 07 Deep Q Network
论文趣读:人工智能里程碑?回顾2015年登上Nature的DQN(全文翻译+批注)
文章:Mnih V , Kavukcuoglu K , Silver D , et al. Playing Atari with Deep Reinforcement Learning[J]. Computer Science, 2013. DeepMind链接:(https://deepmind.com/research/publications/playing-atari-deep-reinforcement-learning)
Piper蛋窝
2020/11/19
1.8K0
论文趣读:人工智能里程碑?回顾2015年登上Nature的DQN(全文翻译+批注)
【综述翻译】Deep Learning for Video Game Playing
原文来源:https://arxiv.org/pdf/1708.07902.pdf
深度强化学习实验室
2020/12/16
1.1K0
【综述翻译】Deep Learning for Video Game Playing
深度强化学习智能交通 (I) :深度强化学习概述
随着城市化进程的加快和自动技术的最新发展,交通研究逐渐向智能化方向发展,称为智能交通系统(ITS)。人工智能(AI)试图用最少的人工干预来控制系统。智能交通系统与人工智能的结合为21世纪的交通研究提供了有效的解决方案。ITS 的主要目标是为参与者提供安全、有效和可靠的交通系统。为此,优化交通信号控制(TSC)、自动车辆控制、交通流控制等是研究的重点。
数据科学人工智能
2022/03/31
1.8K0
深度强化学习智能交通 (I) :深度强化学习概述
从强化学习Reinforcement Learning到DQN(Deep Q-learning Network)学习笔记
本篇博客大概会记录强化学习RL的基础知识,基本方法,以及如何推导到DQN,和关于DeepMind的Playing Atari with Deep Reinforcement Learning(DQN学习打砖块游戏)这篇论文的一些理解,后续改进方向,还有一些具体实现。若有理解不当,恳请指出!
大鹅
2021/06/16
7500
强化学习算法解析:深度 Q 网络(Deep Q - Network,DQN)
强化学习(Reinforcement Learning, RL)是机器学习领域的重要分支,它研究如何让智能体(Agent)通过与环境的交互来学习最优的行为策略。在强化学习中,智能体的目标是最大化长期累积奖励,而环境则根据智能体的行为给出反馈。Q-learning 是强化学习中一种经典的算法,它通过学习状态 - 行动对(State-Action Pair)的 Q 值来指导智能体的行为。然而,传统的 Q-learning 算法在面对状态空间巨大的场景时(如游戏、机器人控制等)存在明显的局限性,因为直接存储和更新所有状态 - 行动对的 Q 值在计算和存储上是不可行的。
jack.yang
2025/04/17
3780
强化学习算法解析:深度 Q 网络(Deep Q - Network,DQN)
深度 | David Silver全面解读深度强化学习:从基础概念到AlphaGo
机器之心原创 作者:Duke Lee 参与:马亚雄、吴攀、吴沁桐、Arac Wu 强化学习在与之相关的研究者中变得越来越流行,尤其是在 DeepMind 被 Google 收购以及 DeepMind 团队在之后的 AlphaGo 上大获成功之后。在本文中,我要回顾一下 David Silver 的演讲。David Silver 目前任职于 Google DeepMind 团队。他的演讲可以帮助我们获得对强化学习(RL)和深度强化学习(Deep RL)的基本理解,这不是一件特别难的事。 David Silve
机器之心
2018/05/07
1K0
深度 | David Silver全面解读深度强化学习:从基础概念到AlphaGo
使用Panda-Gym的机器臂模拟进行Deep Q-learning强化学习
强化学习(RL)是一种机器学习方法,它允许代理通过试错来学习如何在环境中表现。行为主体因采取行动导致预期结果而获得奖励,因采取行动导致预期结果而受到惩罚。随着时间的推移,代理学会采取行动,使其预期回报最大化。
deephub
2023/10/31
5740
使用Panda-Gym的机器臂模拟进行Deep Q-learning强化学习
Deep Q-Learning 简介:一起玩 Doom
上一次,我们了解了 Q-Learning:一种生成 Q-table 的算法,代理使用它来找到在给定状态下采取的最佳动作。
小锋学长生活大爆炸
2021/06/11
7600
Deep Q-Learning 简介:一起玩 Doom
从Q学习到DDPG,一文简述多种强化学习算法
选自towardsdatascience 作者:Steeve Huang 机器之心编译 参与:Edison Ke、路雪 本文简要介绍了强化学习及其重要概念和术语,并着重介绍了 Q-Learning 算法、SARSA、DQN 和 DDPG 算法。 强化学习(RL)指的是一种机器学习方法,其中智能体在下一个时间步中收到延迟的奖励(对前一步动作的评估)。这种方法主要用于雅达利(Atari)、马里奥(Mario)等游戏中,表现与人类相当,甚至超过人类。最近,随着与神经网络的结合,这种算法不断发展,已经能够解决更
企鹅号小编
2018/02/05
1.7K0
从Q学习到DDPG,一文简述多种强化学习算法
DeepMind都拿不下的游戏,刚刚被OpenAI玩出历史最高分
【新智元导读】《蒙特祖玛的复仇》被公认为是Atari中最难的游戏之一,是AI的一大挑战。今天,OpenAI宣布他们的AI仅通过一次人类演示,就在蒙特祖玛的复仇游戏中获得了74500分的成绩,比以往公布的任何结果都要好。而且,这次的成果使用了PPO算法,这正是不久前OpenAI在Dota2 5v5中战胜人类的AI的算法。
新智元
2018/07/31
3250
DeepMind都拿不下的游戏,刚刚被OpenAI玩出历史最高分
强化学习如何入门?看这篇文章就够了
对于大脑的工作原理,我们知之甚少,但是我们知道大脑能通过反复尝试来学习知识。我们做出合适选择时会得到奖励,做出不切当选择时会受到惩罚,这也是我们来适应环境的方式。如今,我们可以利用强大的计算能力,在软件中对这个具体过程进行建模,这就是强化学习。
量子位
2018/07/20
1.1K0
【强化学习】Double DQN(Double Deep Q-Network)算法
强化学习中的深度Q网络(DQN)是一种将深度学习与Q学习结合的算法,它通过神经网络逼近Q函数以解决复杂的高维状态问题。然而,DQN存在过估计问题(Overestimation Bias),即在更新Q值时,由于同时使用同一个网络选择动作和计算目标Q值,可能导致Q值的估计偏高。
不去幼儿园
2025/01/08
1.4K0
【强化学习】Double DQN(Double Deep Q-Network)算法
分享总结 | 叶志豪:介绍强化学习及其在 NLP 上的应用
雷锋网 AI 研习社按:当 AlphaGO 横扫之后,越来越多的学者意识到强化学习在人工智能领域所扮演的重要角色。同时随着深度学习的发展,应用深度学习,很多自然语言的传统难题得到突破。另外,引用 David Silver 的一句话:深度学习 (DL)+ 强化学习 (RL) = 人工智能 (AI)。
AI研习社
2018/07/26
1.6K0
分享总结 | 叶志豪:介绍强化学习及其在 NLP 上的应用
Q-learning与深度Q网络(DQN)
Q-learning 是一种经典的强化学习算法,属于基于值的方法。它的核心思想是通过学习一个状态-动作值函数(Q函数),来帮助智能体在不同状态下选择最佳的动作。Q-learning是一种无模型的强化学习方法,这意味着它不需要了解环境的动态或转移概率,而是通过与环境的交互逐步更新Q值来学习最优策略。
LucianaiB
2025/01/27
1380
强化学习-Reinforcement learning | RL
强化学习算法的思路非常简单,以游戏为例,如果在游戏中采取某种策略可以取得较高的得分,那么就进一步「强化」这种策略,以期继续取得较好的结果。这种策略与日常生活中的各种「绩效奖励」非常类似。我们平时也常常用这样的策略来提高自己的游戏水平。
easyAI
2019/12/18
1.2K0
强化学习-Reinforcement learning | RL
推荐阅读
相关推荐
独家 | 使用Python的OpenAI Gym对Deep Q-Learning的实操介绍(附学习资源)
更多 >
LV.0
这个人很懒,什么都没有留下~
目录
  • 如何启动程序
  • 进入bash: /dev/tty
    • 完整性检查
    • 构建运行环境
  • 进入内核: execve系统调用
    • execve系统调用实现
    • 安全性检查
  • 二进制参数准备
    • struct linux_binprm
    • 分配内存
    • 准备工作
    • 计算命令行参数和环境变量
    • 设置
    • 准备内存相关信息
    • 读取二进制(ELF)文件
  • 处理参数结构
  • 运行
    • 完整性检测
    • 设置地址空间和依赖
    • 开始执行
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档