RunLoop --- CHECK_FOR_FORK()

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_EMBEDDEDDEPLOYMENT_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或类似操作, 否则这些类库可能无法正常运行.

懵逼了, 感觉云里雾里, 可以看一下forkexec的用法, 这个牵涉太多就不在展开, 有兴趣的看这里:

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()是没有任何返回值的.

你可能感兴趣的:(RunLoop --- CHECK_FOR_FORK())