CHECK_FOR_FORK()
写在前面
CHECK_FOR_FORK()
函数在RunLoop的源代码里有很多地方都用到了, 具体是个什么意思呢?
CHECK_FOR_FORK()
在CFRunLoop.m
的定义分为Mac OS(以及嵌入式系统)和其他, 其中的实现逻辑大相径庭
对于Mac OS和嵌入式系统:
系统的定义:
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
extern uint8_t __CF120293;
extern uint8_t __CF120290;
extern void __THE_PROCESS_HAS_FORKED_AND_YOU_CANNOT_USE_THIS_COREFOUNDATION_FUNCTIONALITY___YOU_MUST_EXEC__(void);
#define CHECK_FOR_FORK() do { __CF120290 = true; if (__CF120293) __THE_PROCESS_HAS_FORKED_AND_YOU_CANNOT_USE_THIS_COREFOUNDATION_FUNCTIONALITY___YOU_MUST_EXEC__(); } while (0)
#define CHECK_FOR_FORK_RET(...) do { CHECK_FOR_FORK(); if (__CF120293) return __VA_ARGS__; } while (0)
#define HAS_FORKED() (__CF120293)
#endif
note:
DEPLOYMENT_TARGET_EMBEDDED
和 DEPLOYMENT_TARGET_EMBEDDED_MINI
具体的意思找了很久, 都没有很明白的解释, 字面的意思嵌入式
, 可参见 TARGET Embedded
1. 变量说明:
- __CF120290 控制__CF120293是否可以赋值的开关
- __CF120291 多进程标识
- __CF120293 进程fork标识
涉及到的方法:
//多进程判断
static void __01121__(void) {
__CF120291 = pthread_is_threaded_np() ? true : false;
}
// 设置进程fork标识
static void __01123__(void) {
// Ideally, child-side atfork handlers should be async-cancel-safe, as fork()
// is async-cancel-safe and can be called from signal handlers. See also
// http://standards.ieee.org/reading/ieee/interp/1003-1c-95_int/pasc-1003.1c-37.html
// This is not a problem for CF.
if (__CF120290) {
__CF120293 = true;
#if DEPLOYMENT_TARGET_MACOSX
if (__CF120291) {
CRSetCrashLogMessage2("*** multi-threaded process forked ***");
} else {
CRSetCrashLogMessage2("*** single-threaded process forked ***");
}
#endif
}
}
以上两个放在RunTime
的__CFInitialize()
方法中, 通过pthread_atfork(__01121__, NULL, __01123__);
设置进程fork
前后要执行的方法, 其中__01121__
在fork前调用, 而__01123__
在fork后的子进程中调用.
pthread_atfork解读
2.__THE_PROCESS_HAS_FORKED_AND_YOU_CANNOT_USE_THIS_COREFOUNDATION_FUNCTIONALITY___YOU_MUST_EXEC__
方法
#define EXEC_WARNING_STRING_1 "The process has forked and you cannot use this CoreFoundation functionality safely. You MUST exec().\n"
#define EXEC_WARNING_STRING_2 "Break on __THE_PROCESS_HAS_FORKED_AND_YOU_CANNOT_USE_THIS_COREFOUNDATION_FUNCTIONALITY___YOU_MUST_EXEC__() to debug.\n"
CF_PRIVATE void __THE_PROCESS_HAS_FORKED_AND_YOU_CANNOT_USE_THIS_COREFOUNDATION_FUNCTIONALITY___YOU_MUST_EXEC__(void) {
write(2, EXEC_WARNING_STRING_1, sizeof(EXEC_WARNING_STRING_1) - 1);
write(2, EXEC_WARNING_STRING_2, sizeof(EXEC_WARNING_STRING_2) - 1);
// HALT;
}
这个名字长到令人发指的函数, 其实只干了一件事, 就是向fd=2的句柄(代表打开了一个文件), 写入定义的提示信息, 告诉用户"进程已经被克隆了, 当前的CoreFoundation方法已经不再安全了, 请执行一下exec()置换进程".
Linux系统函数write(strlen、sizeof与write结合使用的区别
3. 总结
CHECK_FOR_FORK()的作用就是打开设置开关__CF120290
, 如果进程进行了fork, 就会在子进程中调用__01123__()
方法, 修改__CF120293__
, 后边的代码便会检测到__CF120293__
为true
, 表明当前进程为fork
后的子线程, 子进程的方法调用已经不再安全了(fork之后子进程同时运行, 使用的还是父进程的数据, 可能存在问题),然后发出提示__THE_PROCESS_HAS_FORKED_AND_YOU_CANNOT_USE_THIS_COREFOUNDATION_FUNCTIONALITY___YOU_MUST_EXEC__
Threading Programming Guide 中有一段描述:
Warning: When launching separate processes using the fork function, you must
always follow a call to fork with a call to exec or a similar function.
Applications that depend on the Core Foundation, Cocoa, or Core Data frameworks
(either explicitly or implicitly) must make a subsequent call to an exec function
or those frameworks may behave improperly.
运行一个调用了fork方法的游离进程(应该指的是子进程), 必须紧接着调用一个exec
或者类似的方法(用于置换子进程), 依赖 Core Foundation, Cocoa, or Core Data 的应用(不管直接或者间接引用)必须执行exec
或类似操作, 否则这些类库可能无法正常运行.
懵逼了, 感觉云里雾里, 可以看一下fork
和exec
的用法, 这个牵涉太多就不在展开, 有兴趣的看这里:
RunLoop 源码阅读
linux进程之fork 和 exec函数
程序员必备知识——fork和exec函数详解
fork和exec的区别
Linux 进程概念
操作系统之 fork() 函数详解
其他 OS定义:
#if !defined(CHECK_FOR_FORK)
#define CHECK_FOR_FORK() do { } while (0)
#endif
#if !defined(CHECK_FOR_FORK_RET)
#define CHECK_FOR_FORK_RET(...) do { } while (0)
#endif
#if !defined(HAS_FORKED)
#define HAS_FORKED() 0
#endif
关于这样的定义有什么用呢?
这个方法应该还没有开源, 应该就是运行安全监测.
iOS App 只有一个进程, 你只能创建线程(参见)
do {...} while (0)
的作用就是: 使用do{...}while(0)
构造后的宏定义不会受到大括号、分号等的影响,总是会按你期望的方式调用运行;
do {...} while (0) 在宏定义中的作用
另外:
What's the meaning of CHECK_FOR_FORK?
感觉说的不太对,CHECK_FOR_FORK()是没有任何返回值的.