为什么我会遇到Linux信号处理的意外行为?(Why am I experiencing unexpected behavior with Linux signal handling?)
我生活在Win7 / MSVC 2010sp1的环境中,两个不同的Linux盒子(Red Hat)和g ++版本(4.4.7,4.1.2),以及AIX和xlc ++(08.00.0000.0025)。
不久前,我们要求将一些代码从AIX移到Linux上。 没过多久就看到Linux有点不同了。 通常,当抛出信号时,我们处理它并抛出C ++异常。 这没有按预期工作。
过了一段时间,我整理了一个使用setjmp / longjmp将异常移出处理程序的修复程序。 在所有平台上都可以进行后续测试。 经过一场强制性的立方体快乐舞会后,我开始进行一些单元测试。 哎呀。
我的一些测试在Linux上失败了。 我观察到的是,提升功能只能工作一次。 使用SIGILL进行了两次测试,第一次通过,第二次失败。 我打破了一把斧头,并开始砍掉代码以尽可能多地删除斧头。 这产生了这个较小的例子。
#include <csetjmp> #include <iostream> #include <signal.h> jmp_buf mJmpBuf; jmp_buf *mpJmpBuf = &mJmpBuf; int status = 0; int testCount = 3; void handler(int signalNumber) { signal(signalNumber, handler); longjmp(*mpJmpBuf, signalNumber); } int main(void) { if (signal(SIGILL, handler) != SIG_ERR) { for (int test = 1; test <= testCount; test++) { try { std::cerr << "Test " << test << "\n"; if ((status = setjmp(*mpJmpBuf)) == 0) { std::cerr << " About to raise SIGILL" << "\n"; int returnStatus = raise(SIGILL); std::cerr << " Raise returned value " << returnStatus << "\n"; } else { std::cerr << " Caught signal. Converting signal " << status << " to exception" << "\n"; std::exception e; throw e; } std::cerr << " SIGILL should have been thrown **********\n"; } catch (std::exception &) { std::cerr << " Caught exception as expected\n"; } } } else { std::cerr << "The signal handler wasn't registered\n"; } return 0; }
对于Windows和AIX框,我得到了预期的输出。
Test 1 About to raise SIGILL Caught signal. Converting signal 4 to exception Caught exception as expected Test 2 About to raise SIGILL Caught signal. Converting signal 4 to exception Caught exception as expected Test 3 About to raise SIGILL Caught signal. Converting signal 4 to exception Caught exception as expected
对于两个Linux盒子,它看起来像这样。
Test 1 About to raise SIGILL Caught signal. Converting signal 4 to exception Caught exception as expected Test 2 About to raise SIGILL Raise returned value 0 SIGILL should have been thrown ********** Test 3 About to raise SIGILL Raise returned value 0 SIGILL should have been thrown **********
所以,我真正的问题是“ 这里发生了什么? ”
我的修辞问题是:
- 还有其他人观察到这种行为吗?
- 我该怎么做才能解决这个问题?
- 我应该注意哪些其他事情?
I live in an environment with Win7/MSVC 2010sp1, two different Linux boxes (Red Hat) with g++ versions (4.4.7, 4.1.2), and AIX with xlc++ (08.00.0000.0025).
Not so long ago it was requested that we move some code from AIX over to Linux. It didn't take too long to see that Linux was a bit different. Normally when a signal is thrown, we handle it and throw a C++ exception. That was not working as expected.
Long story short, throwing c++ exceptions from a signal handler isn't going to work.
Sometime later, I put together a fix that uses setjmp/longjmp to move the exception out of the handler. Aftersome testing and the dang thing works on all platforms. After an obligatory round of cubical happy dance I moved on to setting up some unit tests. Oops.
Some of my tests were failing on Linux. What I observed was that the raise function only worked once. With two tests using SIGILL, the first one passed, and the second one failed. I broke out an axe, and started chopping away at the code to remove as much cruft as possible. That yielded this smaller example.
#include <csetjmp> #include <iostream> #include <signal.h> jmp_buf mJmpBuf; jmp_buf *mpJmpBuf = &mJmpBuf; int status = 0; int testCount = 3; void handler(int signalNumber) { signal(signalNumber, handler); longjmp(*mpJmpBuf, signalNumber); } int main(void) { if (signal(SIGILL, handler) != SIG_ERR) { for (int test = 1; test <= testCount; test++) { try { std::cerr << "Test " << test << "\n"; if ((status = setjmp(*mpJmpBuf)) == 0) { std::cerr << " About to raise SIGILL" << "\n"; int returnStatus = raise(SIGILL); std::cerr << " Raise returned value " << returnStatus << "\n"; } else { std::cerr << " Caught signal. Converting signal " << status << " to exception" << "\n"; std::exception e; throw e; } std::cerr << " SIGILL should have been thrown **********\n"; } catch (std::exception &) { std::cerr << " Caught exception as expected\n"; } } } else { std::cerr << "The signal handler wasn't registered\n"; } return 0; }
For the Windows and the AIX boxes I get the expected output.
Test 1 About to raise SIGILL Caught signal. Converting signal 4 to exception Caught exception as expected Test 2 About to raise SIGILL Caught signal. Converting signal 4 to exception Caught exception as expected Test 3 About to raise SIGILL Caught signal. Converting signal 4 to exception Caught exception as expected
For both Linux boxes it looks like this.
Test 1 About to raise SIGILL Caught signal. Converting signal 4 to exception Caught exception as expected Test 2 About to raise SIGILL Raise returned value 0 SIGILL should have been thrown ********** Test 3 About to raise SIGILL Raise returned value 0 SIGILL should have been thrown **********
So, my real question is "What is going on here?"
My retorical questions are:
- Is anyone else observing this behavior?
- What should I do to try to troubleshoot this issue?
- What other things should I be aware of?
原文:https://stackoverflow.com/questions/28005187
最满意答案
相关问答
更多-
您可以在能够运行查询的任何地方运行过程代码。 只需复制AS后的所有内容: BEGIN DECLARE @myvar INT SELECT * FROM mytable WHERE @myvar ... END 此代码与存储过程完全相同,但不会存储在数据库端。 这很像PL/SQL所谓的匿名过程。 更新: 你的问题有点混乱。 如果你只需要创建一个程序,如果它不存在,那么你的代码很好。 以下是创建脚本中SSMS输出的内容: IF EXISTS ( SELECT * ...
-
你正在打开游标,但是你并没有将它读入任何东西 - 无论是一系列标量变量还是一个记录类型 - 通常会在循环中完成。 然后当你在循环中时,你引用变量/记录,而不是在游标查询中使用的表 - 超出了游标声明之外的范围。 有一个稍微简单的隐式游标循环,在这种情况下可能会更容易一些: CREATE OR REPLACE PROCEDURE storedprocedure(emp number) AS BEGIN FOR rec IN ( select e.employeeid, firstname, las ...
-
你能否改变你的过程来包含EXECUTE AS子句? 例: ALTER PROCEDURE sprocName... EXECUTE AS dbo -- high level example ... 这将允许您仅授予您的用户和角色执行程序的能力,但程序能够执行所需的任务,即选择,插入,更新等。 Can you alter your procedure to include the EXECUTE AS clause? Example: ALTER PROCEDURE sprocName... EXECUTE ...
-
您已将TestUser绑定到localhost,这就是他无法远程访问(来自其他IP)的原因。 如果您希望Testuser远程访问,您需要指定该IP或只使用通配符(%),以便他可以从任何地方(localhost或不是)访问它。 You have bound TestUser to the localhost that is why he cannot access remotely (from another IP ). If you want Testuser to access remotely you ...
-
关于存储过程中的查询(Regarding query in stored procedure)[2022-12-09]
过滤记录时,您没有考虑过CourseId。 您的查询: SELECT @stud = (select Count(enrollmentId) from CourseEnrollment where StudentId = @StudentId ) 如果你已经通过课程1为学生xyz说,如果xyz已经注册了课程2,那么你的查询将给出结果为1,即使他没有注册课程1,你会说他已经注册了相同的课程。 尝试以下方法: IF EXISTS (SELE ... -
授予创建存储过程,查看和使用选择查询的权限(Grant permission for creating stored procedure, view and using Select query)[2021-06-30]
GRANT CREATE PROCEDURE TO [login]; GRANT CREATE VIEW TO [login]; GRANT SELECT TO [login]; 等等 GRANT CREATE PROCEDURE TO [login]; GRANT CREATE VIEW TO [login]; GRANT SELECT TO [login]; And so on -
这在MySQL文档存储程序和视图的访问控制中有详细解释。 可以使用以下任一方法定义存储过程: SQL SECURITY DEFINER 要么 SQL SECURITY INVOKER 如果它是DEFINER ,则该过程使用定义该过程的用户的权限执行; 在您的情况下,如果mysql_user_1具有SELECT权限,则该过程适用于mysql_user_2 。 如果它是INVOKER ,则该过程使用运行该过程的用户的权限执行。 在这种情况下,该过程不适用于mysql_user_2 。 如果程序中没有此子句, ...
-
GO不能在动态sql中使用。 你可以像这样使用嵌套的SQL查询 DECLARE @dbName nvarchar(30) = 'abcxys' DECLARE @query nvarchar(max) = 'USE ' + Quotename(@dbName) + '; EXEC sp_executesql N''' + N'CREATE PROCEDURE [dbo].[usp_GetUser] @id varchar(50) AS BEGIN ...
-
将执行权限授予对基础表没有SELECT权限的存储过程(Grant execute to stored procedure without SELECT permissions to underlying table)[2023-09-12]
Weicher,除非在基础表上有特定的否定,否则此授权声明应该有效: GRANT Exec [dbo].[myproc] TO MyUser; GO 用这样的代码测试它: EXECUTE AS USER = 'MyUser'; GO EXEC [dbo].[myproc]; -- should work GO SELECT id FROM [dbo].[table]; -- should fail GO REVERT; 当我将代码改为星期日的六种方式时,我喜欢开始清洁,以确保我不会无意中发生任何问题并且 ... -
是的,您可以在声明存储过程时使用sql security definer来执行此操作: SQL SECURITY特性可以是DEFINER或INVOKER来指定安全上下文; 也就是说,例程是使用例程DEFINER子句中指定的帐户的权限还是使用调用它的用户执行。 此帐户必须具有访问与例程关联的数据库的权限。 默认值为DEFINER。 调用例程的用户必须具有EXECUTE特权,如果例程在definer安全上下文中执行,则DEFINER帐户必须具有该特权。 DEFINER子句指定在具有SQL SECURITY DE ...