task_vfork.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. /****************************************************************************
  2. * sched/task/task_vfork
  3. *
  4. * Copyright (C) 2013-2014, 2019 Gregory Nutt. All rights reserved.
  5. * Author: Gregory Nutt <gnutt@nuttx.org>
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in
  15. * the documentation and/or other materials provided with the
  16. * distribution.
  17. * 3. Neither the name NuttX nor the names of its contributors may be
  18. * used to endorse or promote products derived from this software
  19. * without specific prior written permission.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  24. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  25. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  26. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  27. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  28. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  29. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  31. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  32. * POSSIBILITY OF SUCH DAMAGE.
  33. *
  34. ****************************************************************************/
  35. /****************************************************************************
  36. * Included Files
  37. ****************************************************************************/
  38. #include <nuttx/config.h>
  39. #include <sys/wait.h>
  40. #include <stdint.h>
  41. #include <sched.h>
  42. #include <string.h>
  43. #include <assert.h>
  44. #include <errno.h>
  45. #include <queue.h>
  46. #include <debug.h>
  47. #include <nuttx/sched.h>
  48. #include "sched/sched.h"
  49. #include "group/group.h"
  50. #include "task/task.h"
  51. /****************************************************************************
  52. * Pre-processor Definitions
  53. ****************************************************************************/
  54. /* vfork() requires architecture-specific support as well as waipid(). */
  55. #if defined(CONFIG_ARCH_HAVE_VFORK) && defined(CONFIG_SCHED_WAITPID)
  56. /* This is an artificial limit to detect error conditions where an argv[]
  57. * list is not properly terminated.
  58. */
  59. #define MAX_VFORK_ARGS 256
  60. /****************************************************************************
  61. * Private Functions
  62. ****************************************************************************/
  63. /****************************************************************************
  64. * Name: nxvfork_namesetup
  65. *
  66. * Description:
  67. * Copy the task name.
  68. *
  69. * Input Parameters:
  70. * tcb - Address of the new task's TCB
  71. * name - Name of the new task
  72. *
  73. * Returned Value:
  74. * None
  75. *
  76. ****************************************************************************/
  77. #if CONFIG_TASK_NAME_SIZE > 0
  78. static inline void nxvfork_namesetup(FAR struct tcb_s *parent,
  79. FAR struct task_tcb_s *child)
  80. {
  81. /* Copy the name from the parent into the child TCB */
  82. strncpy(child->cmn.name, parent->name, CONFIG_TASK_NAME_SIZE);
  83. }
  84. #else
  85. # define nxvfork_namesetup(p,c)
  86. #endif /* CONFIG_TASK_NAME_SIZE */
  87. /****************************************************************************
  88. * Name: nxvfork_stackargsetup
  89. *
  90. * Description:
  91. * Clone the task arguments in the same relative positions on the child's
  92. * stack.
  93. *
  94. * Input Parameters:
  95. * parent - Address of the parent task's TCB
  96. * child - Address of the child task's TCB
  97. *
  98. * Returned Value:
  99. * Zero (OK) on success; a negated errno on failure.
  100. *
  101. ****************************************************************************/
  102. static inline int nxvfork_stackargsetup(FAR struct tcb_s *parent,
  103. FAR struct task_tcb_s *child)
  104. {
  105. /* Is the parent a task? or a pthread? Only tasks (and kernel threads)
  106. * have command line arguments.
  107. */
  108. child->argv = NULL;
  109. if ((parent->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_PTHREAD)
  110. {
  111. FAR struct task_tcb_s *ptcb = (FAR struct task_tcb_s *)parent;
  112. uintptr_t offset;
  113. int argc;
  114. /* Get the address correction */
  115. offset = (uintptr_t)child->cmn.adj_stack_ptr -
  116. (uintptr_t)parent->adj_stack_ptr;
  117. /* Change the child argv[] to point into its stack (instead of its
  118. * parent's stack).
  119. */
  120. child->argv = (FAR char **)((uintptr_t)ptcb->argv + offset);
  121. /* Copy the adjusted address for each argument */
  122. argc = 0;
  123. while (ptcb->argv[argc])
  124. {
  125. uintptr_t newaddr = (uintptr_t)ptcb->argv[argc] + offset;
  126. child->argv[argc] = (FAR char *)newaddr;
  127. /* Increment the number of args. Here is a sanity check to
  128. * prevent running away with an unterminated argv[] list.
  129. * MAX_VFORK_ARGS should be sufficiently large that this never
  130. * happens in normal usage.
  131. */
  132. if (++argc > MAX_VFORK_ARGS)
  133. {
  134. return -E2BIG;
  135. }
  136. }
  137. /* Put a terminator entry at the end of the child argv[] array. */
  138. child->argv[argc] = NULL;
  139. }
  140. return OK;
  141. }
  142. /****************************************************************************
  143. * Name: nxvfork_argsetup
  144. *
  145. * Description:
  146. * Clone the argument list from the parent to the child.
  147. *
  148. * Input Parameters:
  149. * parent - Address of the parent task's TCB
  150. * child - Address of the child task's TCB
  151. *
  152. * Returned Value:
  153. * Zero (OK) on success; a negated errno on failure.
  154. *
  155. ****************************************************************************/
  156. static inline int nxvfork_argsetup(FAR struct tcb_s *parent,
  157. FAR struct task_tcb_s *child)
  158. {
  159. /* Clone the task name */
  160. nxvfork_namesetup(parent, child);
  161. /* Adjust and copy the argv[] array. */
  162. return nxvfork_stackargsetup(parent, child);
  163. }
  164. /****************************************************************************
  165. * Name: nxvfork_argsize
  166. *
  167. * Description:
  168. * Get the parent's argument size.
  169. *
  170. * Input Parameters:
  171. * parent - Address of the parent task's TCB
  172. *
  173. * Return Value:
  174. * The parent's argument size.
  175. *
  176. ****************************************************************************/
  177. static inline size_t nxvfork_argsize(FAR struct tcb_s *parent)
  178. {
  179. if ((parent->flags & TCB_FLAG_TTYPE_MASK) != TCB_FLAG_TTYPE_PTHREAD)
  180. {
  181. FAR struct task_tcb_s *ptcb = (FAR struct task_tcb_s *)parent;
  182. size_t strtablen = 0;
  183. int argc = 0;
  184. while (ptcb->argv[argc])
  185. {
  186. /* Add the size of this argument (with NUL terminator) */
  187. strtablen += strlen(ptcb->argv[argc++]) + 1;
  188. }
  189. /* Return the size to hold argv[] array and the strings. */
  190. return (argc + 1) * sizeof(FAR char *) + strtablen;
  191. }
  192. else
  193. {
  194. return 0;
  195. }
  196. }
  197. /****************************************************************************
  198. * Public Functions
  199. ****************************************************************************/
  200. /****************************************************************************
  201. * Name: nxtask_vforksetup
  202. *
  203. * Description:
  204. * The vfork() function has the same effect as fork(), except that the
  205. * behavior is undefined if the process created by vfork() either modifies
  206. * any data other than a variable of type pid_t used to store the return
  207. * value from vfork(), or returns from the function in which vfork() was
  208. * called, or calls any other function before successfully calling _exit()
  209. * or one of the exec family of functions.
  210. *
  211. * This function provides one step in the overall vfork() sequence: It
  212. * Allocates and initializes the child task's TCB. The overall sequence is:
  213. *
  214. * 1) User code calls vfork(). vfork() is provided in architecture-specific
  215. * code.
  216. * 2) vfork()and calls nxtask_vforksetup().
  217. * 3) nxtask_vforksetup() allocates and configures the child task's TCB. This
  218. * consists of:
  219. * - Allocation of the child task's TCB.
  220. * - Initialization of file descriptors and streams
  221. * - Configuration of environment variables
  222. * - Setup the input parameters for the task.
  223. * - Initialization of the TCB (including call to up_initial_state()
  224. * 4) up_vfork() provides any additional operating context. up_vfork must:
  225. * - Allocate and initialize the stack
  226. * - Initialize special values in any CPU registers that were not
  227. * already configured by up_initial_state()
  228. * 5) up_vfork() then calls nxtask_vforkstart()
  229. * 6) nxtask_vforkstart() then executes the child thread.
  230. *
  231. * Input Parameters:
  232. * retaddr - Return address
  233. * argsize - Location to return the argument size
  234. *
  235. * Returned Value:
  236. * Upon successful completion, nxtask_vforksetup() returns a pointer to
  237. * newly allocated and initialized child task's TCB. NULL is returned
  238. * on any failure and the errno is set appropriately.
  239. *
  240. ****************************************************************************/
  241. FAR struct task_tcb_s *nxtask_vforksetup(start_t retaddr, size_t *argsize)
  242. {
  243. struct tcb_s *parent = this_task();
  244. struct task_tcb_s *child;
  245. uint8_t ttype;
  246. int priority;
  247. int ret;
  248. DEBUGASSERT(retaddr != NULL && argsize != NULL);
  249. /* Get the type of the fork'ed task (kernel or user) */
  250. if ((parent->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_KERNEL)
  251. {
  252. /* Fork'ed from a kernel thread */
  253. ttype = TCB_FLAG_TTYPE_KERNEL;
  254. }
  255. else
  256. {
  257. /* Fork'ed from a user task or pthread */
  258. ttype = TCB_FLAG_TTYPE_TASK;
  259. }
  260. /* Allocate a TCB for the child task. */
  261. child = (FAR struct task_tcb_s *)kmm_zalloc(sizeof(struct task_tcb_s));
  262. if (!child)
  263. {
  264. serr("ERROR: Failed to allocate TCB\n");
  265. set_errno(ENOMEM);
  266. return NULL;
  267. }
  268. /* Allocate a new task group with the same privileges as the parent */
  269. ret = group_allocate(child, parent->flags);
  270. if (ret < 0)
  271. {
  272. goto errout_with_tcb;
  273. }
  274. /* Associate file descriptors with the new task */
  275. ret = group_setuptaskfiles(child);
  276. if (ret < OK)
  277. {
  278. goto errout_with_tcb;
  279. }
  280. /* Get the priority of the parent task */
  281. #ifdef CONFIG_PRIORITY_INHERITANCE
  282. priority = parent->base_priority; /* "Normal," unboosted priority */
  283. #else
  284. priority = parent->sched_priority; /* Current priority */
  285. #endif
  286. /* Initialize the task control block. This calls up_initial_state() */
  287. sinfo("Child priority=%d start=%p\n", priority, retaddr);
  288. ret = nxtask_schedsetup(child, priority, retaddr, parent->entry.main, ttype);
  289. if (ret < OK)
  290. {
  291. goto errout_with_tcb;
  292. }
  293. /* Return the argument size */
  294. *argsize = nxvfork_argsize(parent);
  295. sinfo("parent=%p, returning child=%p\n", parent, child);
  296. return child;
  297. errout_with_tcb:
  298. sched_releasetcb((FAR struct tcb_s *)child, ttype);
  299. set_errno(-ret);
  300. return NULL;
  301. }
  302. /****************************************************************************
  303. * Name: nxtask_vforkstart
  304. *
  305. * Description:
  306. * The vfork() function has the same effect as fork(), except that the
  307. * behavior is undefined if the process created by vfork() either modifies
  308. * any data other than a variable of type pid_t used to store the return
  309. * value from vfork(), or returns from the function in which vfork() was
  310. * called, or calls any other function before successfully calling _exit()
  311. * or one of the exec family of functions.
  312. *
  313. * This function provides one step in the overall vfork() sequence: It
  314. * starts execution of the previously initialized TCB. The overall
  315. * sequence is:
  316. *
  317. * 1) User code calls vfork()
  318. * 2) Architecture-specific code provides vfork()and calls
  319. * nxtask_vforksetup().
  320. * 3) nxtask_vforksetup() allocates and configures the child task's TCB.
  321. * This consists of:
  322. * - Allocation of the child task's TCB.
  323. * - Initialization of file descriptors and streams
  324. * - Configuration of environment variables
  325. * - Setup the input parameters for the task.
  326. * - Initialization of the TCB (including call to up_initial_state()
  327. * 4) vfork() provides any additional operating context. vfork must:
  328. * - Allocate and initialize the stack
  329. * - Initialize special values in any CPU registers that were not
  330. * already configured by up_initial_state()
  331. * 5) vfork() then calls nxtask_vforkstart()
  332. * 6) nxtask_vforkstart() then executes the child thread.
  333. *
  334. * Input Parameters:
  335. * retaddr - The return address from vfork() where the child task
  336. * will be started.
  337. *
  338. * Returned Value:
  339. * Upon successful completion, vfork() returns 0 to the child process and
  340. * returns the process ID of the child process to the parent process.
  341. * Otherwise, -1 is returned to the parent, no child process is created,
  342. * and errno is set to indicate the error.
  343. *
  344. ****************************************************************************/
  345. pid_t nxtask_vforkstart(FAR struct task_tcb_s *child)
  346. {
  347. struct tcb_s *parent = this_task();
  348. pid_t pid;
  349. int rc;
  350. int ret;
  351. sinfo("Starting Child TCB=%p, parent=%p\n", child, this_task());
  352. DEBUGASSERT(child);
  353. /* Duplicate the original argument list in the forked child TCB */
  354. ret = nxvfork_argsetup(parent, child);
  355. if (ret < 0)
  356. {
  357. nxtask_vforkabort(child, -ret);
  358. return ERROR;
  359. }
  360. /* Now we have enough in place that we can join the group */
  361. ret = group_initialize(child);
  362. if (ret < 0)
  363. {
  364. nxtask_vforkabort(child, -ret);
  365. return ERROR;
  366. }
  367. /* Get the assigned pid before we start the task */
  368. pid = (int)child->cmn.pid;
  369. /* Eliminate a race condition by disabling pre-emption. The child task
  370. * can be instantiated, but cannot run until we call waitpid(). This
  371. * assures us that we cannot miss the death-of-child signal (only
  372. * needed in the SMP case).
  373. */
  374. sched_lock();
  375. /* Activate the task */
  376. ret = task_activate((FAR struct tcb_s *)child);
  377. if (ret < OK)
  378. {
  379. nxtask_vforkabort(child, -ret);
  380. sched_unlock();
  381. return ERROR;
  382. }
  383. /* The child task has not yet ran because pre-emption is disabled.
  384. * The child task has the same priority as the parent task, so that
  385. * would typically be the case anyway. However, in the SMP
  386. * configuration, the child thread might have already ran on
  387. * another CPU if pre-emption were not disabled.
  388. *
  389. * It is a requirement that the parent environment be stable while
  390. * vfork runs; the child thread is still dependent on things in the
  391. * parent thread... like the pointers into parent thread's stack
  392. * which will still appear in the child's registers and environment.
  393. *
  394. * We assure that by waiting for the child thread to exit before
  395. * returning to the parent thread. NOTE that pre-emption will be
  396. * re-enabled while we are waiting, giving the child thread the
  397. * opportunity to run.
  398. */
  399. rc = 0;
  400. #ifdef CONFIG_DEBUG_FEATURES
  401. ret = waitpid(pid, &rc, 0);
  402. if (ret < 0)
  403. {
  404. serr("ERROR: waitpid failed: %d\n", errno);
  405. }
  406. #else
  407. (void)waitpid(pid, &rc, 0);
  408. #endif
  409. sched_unlock();
  410. return pid;
  411. }
  412. /****************************************************************************
  413. * Name: nxtask_vforkabort
  414. *
  415. * Description:
  416. * Recover from any errors after nxtask_vforksetup() was called.
  417. *
  418. * Returned Value:
  419. * None
  420. *
  421. ****************************************************************************/
  422. void nxtask_vforkabort(FAR struct task_tcb_s *child, int errcode)
  423. {
  424. /* The TCB was added to the active task list by nxtask_schedsetup() */
  425. dq_rem((FAR dq_entry_t *)child, (FAR dq_queue_t *)&g_inactivetasks);
  426. /* Release the TCB */
  427. sched_releasetcb((FAR struct tcb_s *)child,
  428. child->cmn.flags & TCB_FLAG_TTYPE_MASK);
  429. set_errno(errcode);
  430. }
  431. #endif /* CONFIG_ARCH_HAVE_VFORK && CONFIG_SCHED_WAITPID */