关于might_sleep的一点说明

might_sleep(): 指示当前函数可以睡眠。如果它所在的函数处于原子上下文(atomic context)中(如,spinlock, irq-handler…),将打印出堆栈的回溯信息。这个函数主要用来做调试工作,在你不确定不期望睡眠的地方是否真的不会睡眠时,就把这个宏加进去。


简单地说,如果没有调试的需求(绝大多数下你平常跑的系统都是release版本的kernel),那么这个宏(或者函数,称谓并不重要)什么实质性的活都不干,内核只是用它来做一件事,就是提醒你,调用该函数的函数可能会sleep,这个跟其名字也是匹配的: The function calling might_sleep() might sleep。如果你想看源码,我把它列在下面:

<include/linux/kernel.h>

点击(此处)折叠或打开

  1. # define might_resched() do { } while (0)
  2. # define might_sleep() do { might_resched(); } while (0)
看到没,啥事都没干。其实内核源码对此也有明确的注释:might_sleep - annotation for functions that can sleep。所以对于release版的kernel image而言,might_sleep的作用仅仅是一个annotation,提醒使用者,一个使用might_sleep的函数在其后的代码执行中可能会sleep。

不过如果有调试需求介入的话,比如你的系统莫名其妙地随机性地crash掉,在经过一段艰难的案情分析排查之后,最后你决定打开内核的 CONFIG_DEBUG_ATOMIC_SLEEP选项,那么此时might_sleep对案情的进一步推进就可能产生贡献了。 CONFIG_DEBUG_ATOMIC_SLEEP选项主要用来排查是否在一个ATOMIC操作的上下文中有函数发生sleep行为,关于什么是 ATOMIC操作,内核源码在might_sleep函数前也有一段注释:
this macro will print a stack trace if it is executed in an atomic context (spinlock, irq-handler, ...)

所以很明显,一个进程获得了spinlock之后它就进入了这里所谓的atomic context,或者是在一个irq-handler,也就是一个中断上下文中。这两种上下文中理论上不应该让当前的execution path进入sleep状态(虽然不是强制规定,换句话说,一个拥有spinlock的进程进入sleep并不必然意味着系统就一定会deadlock 等,但是对内核编程而言,还是应该尽力避开这个雷区)。

在CONFIG_DEBUG_ATOMIC_SLEEP选项打开的情形下,might_sleep又有哪些特殊的功能呢?先看看内核中的源码:

<kernel/sched.c>

点击(此处)折叠或打开

  1. void __might_sleep(const char *file, int line, int preempt_offset)
  2. {
  3.         static unsigned long prev_jiffy; /* ratelimiting */

  4.         if ((preempt_count_equals(preempt_offset) && !irqs_disabled()) ||
  5.             system_state != SYSTEM_RUNNING || oops_in_progress)
  6.                 return;
  7.         if (time_before(jiffies, prev_jiffy + HZ) && prev_jiffy)
  8.                 return;
  9.         prev_jiffy = jiffies;

  10.         printk(KERN_ERR
  11.                 "BUG: sleeping function called from invalid context at %s:%d\n",
  12.                         file, line);
  13.         printk(KERN_ERR
  14.                 "in_atomic(): %d, irqs_disabled(): %d, pid: %d, name: %s\n",
  15.                         in_atomic(), irqs_disabled(),
  16.                         current->pid, current->comm);

  17.         if (irqs_disabled())
  18.                 print_irqtrace_events(current);
  19.         dump_stack();
  20. }
上面的代码我进行了轻微的删减,去除了一些只有 CONFIG_DEBUG_ATOMIC_SLEEP选项使能的情形下不干活的函数。

<include/linux/kernel.h>

点击(此处)折叠或打开

  1. # define might_sleep() \
  2.         do { __might_sleep(__FILE__, __LINE__, 0); might_resched(); } while (0)
在当前CONFIG_DEBUG_ATOMIC_SLEEP选项使能的前提下, 可以看到__might_sleep还是干了不少事情的,最主要的工作是在第一个if语句那里,尤其是preempt_count_equals和 irqs_disabled,都是用来判断当前的上下文是否是一个atomic context,因为我们知道,只要进程获得了spin_lock的任一个变种形式的lock,那么无论是单处理器系统还是多处理器系统,都会导致 preempt_count发生变化,而irq_disabled则是用来判断当前中断是否开启。__might_sleep正是根据这些信息来判断当前正在执行的代码上下文是否是个atomic,如果不是,那么函数就直接返回了,因为一切正常。如果是,那么代码下行。

所以让CONFIG_DEBUG_ATOMIC_SLEEP选项打开,可以捕捉到在一个atomic context中是否发生了sleep,如果你的代码不小心在某处的确出现了这种情形,那么might_sleep会通过后续的printk以及dump_stack来协助你发现这种情形。


至于__might_sleep函数中的system_state,它是一个全局性的enum型变量,主要用来记录当前系统的状态:
<init/main.c>

点击(此处)折叠或打开

  1. enum system_states system_state __read_mostly;
  2. EXPORT_SYMBOL(system_state);
注意system_state已经被export出来,所以内核模块可以直接读该值来判断当前系统的运行状态,常见的状态包括:
<include/linux/kernel.h>

点击(此处)折叠或打开

  1. extern enum system_states {
  2.     SYSTEM_BOOTING,
  3.     SYSTEM_RUNNING,
  4.     SYSTEM_HALT,
  5.     SYSTEM_POWER_OFF,
  6.     SYSTEM_RESTART,
  7.     SYSTEM_SUSPEND_DISK,
  8. } system_state;
最常见的状态当然是SYSTEM_RUNNING了,你的系统正常起来之后就处于这个状态。因为跟当前的话题没有直接的关联,这里只提一下好了。

### 如何使用 PyBullet 实现机械臂抓取物体的仿真 #### 了解基础概念 为了成功实现机械臂在 PyBullet 中的抓取操作,理解机器人正逆运动学基本概念至关重要[^1]。这涉及到如何定义关节角度来达到特定末端执行器位置(逆向运动学),以及给定关节配置时预测末端执行器的位置(正向运动学)。 #### 设置环境与加载模型 利用 `pybullet_ur5_robotiq` 项目作为起点是一个不错的选择[^2]。此资源提供了一个预构建的 UR5 臂及其夹爪,在 Bullet 模拟器内的设置方法如下: ```python import pybullet as p import pybullet_data import time p.connect(p.GUI) p.setAdditionalSearchPath(pybullet_data.getDataPath()) planeId = p.loadURDF("plane.urdf") robot_id = p.loadURDF( "ur_e_description/urdf/ur5e_with_robotiq_85.urdf", basePosition=[0, 0, 0], useFixedBase=True, ) for i in range(7): # Set all joints to zero position initially. p.resetJointState(robot_id, i, targetValue=0) ``` 这段代码初始化了 PyBullet GUI 并导入了平面和平移缩放版 UR5 及其配套的手指装置。 #### 定义目标姿态并移动到该处 对于想要让机械手去往的空间中某一点,可以通过求解逆向运动学获得所需的关节角。这里展示了一种简单的方式来做这件事: ```python target_position = [0.4, 0.2, 0.1] # XYZ coordinates of the target point above an object. joint_positions = p.calculateInverseKinematics( robot_id, endEffectorLinkIndex=9, # Index may vary depending on your model's structure. targetPosition=target_position, lowerLimits=[-3.14]*6, upperLimits=[3.14]*6, jointRanges=[6.28]*6, restPoses=[0]*6, ) for i, pos in enumerate(joint_positions[:6]): p.setJointMotorControl2( bodyUniqueId=robot_id, jointIndex=i, controlMode=p.POSITION_CONTROL, targetPosition=pos, force=500, ) time.sleep(2) # Wait for movement completion before proceeding further actions like grasping objects. ``` 上述脚本片段展示了怎样设定一个空间坐标作为目的地点,并通过调用 `calculateInverseKinematics()` 函数计算出对应于这些坐标的合适关节角度组合;之后再把这些值应用至各自由度上完成动作指令发送。 #### 抓握物品 当接近待拾起的目标后,则需激活手指闭合功能以实施抓取行为。具体做法取决于所使用的夹具类型,但对于 Robotiq Gripper 来说,通常涉及控制两个主要驱动轴——负责开合的动作伺服电机。 ```python gripper_joint_indices = [10, 11] # Adjust according to actual setup; these are common indices but might differ based on urdf file used. def close_gripper(): """Closes the gripper fingers around any nearby item.""" for idx in gripper_joint_indices: p.setJointMotorControl2( bodyUniqueId=robot_id, jointIndex=idx, controlMode=p.POSITION_CONTROL, targetPosition=-0.02, # Negative value closes the finger. maxVelocity=0.05, force=100, ) close_gripper() time.sleep(2) # Allow enough time for closing motion. ``` 以上就是关于如何借助 PyBullet 库创建一个简单的机械手臂抓取仿真的指导说明。当然,实际应用场景会更加复杂,可能还需要考虑诸如视觉感知、力反馈等因素。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值