概述
最近在整理多线程之间的同步,偶然发现一个例子里面使用到了fork,所以整理一下fork的使用笔记
fork() 主要的作用就是创建一个子进程。
1. 函数原型以及机翻
NAME
fork - create a child process
SYNOPSIS
#include <sys/types.h>
#include <unistd.h>
pid_t fork(void);
DESCRIPTION
fork() creates a new process by duplicating the calling process. The new process is referred to as the child process. The calling process is referred to as the parent process.
The child process and the parent process run in separate memory spaces. At the time of fork() both memory spaces have the same content. Memory writes, file mappings (mmap(2)), and
unmappings (munmap(2)) performed by one of the processes do not affect the other.
The child process is an exact duplicate of the parent process except for the following points:
* The child has its own unique process ID, and this PID does not match the ID of any existing process group (setpgid(2)) or session.
* The child's parent process ID is the same as the parent's process ID.
* The child does not inherit its parent's memory locks (mlock(2), mlockall(2)).
* Process resource utilizations (getrusage(2)) and CPU time counters (times(2)) are reset to zero in the child.
* The child's set of pending signals is initially empty (sigpending(2)).
* The child does not inherit semaphore adjustments from its parent (semop(2)).
* The child does not inherit process-associated record locks from its parent (fcntl(2)). (On the other hand, it does inherit fcntl(2) open file description locks and flock(2) locks
from its parent.)
* The child does not inherit timers from its parent (setitimer(2), alarm(2), timer_create(2)).
* The child does not inherit outstanding asynchronous I/O operations from its parent (aio_read(3), aio_write(3)), nor does it inherit any asynchronous I/O contexts from its parent
(see io_setup(2)).
The process attributes in the preceding list are all specified in POSIX.1. The parent and child also differ with respect to the following Linux-specific process attributes:
* The child does not inherit directory change notifications (dnotify) from its parent (see the description of F_NOTIFY in fcntl(2)).
* The prctl(2) PR_SET_PDEATHSIG setting is reset so that the child does not receive a signal when its parent terminates.
* The default timer slack value is set to the parent's current timer slack value. See the description of PR_SET_TIMERSLACK in prctl(2).
* Memory mappings that have been marked with the madvise(2) MADV_DONTFORK flag are not inherited across a fork().
* Memory in address ranges that have been marked with the madvise(2) MADV_WIPEONFORK flag is zeroed in the child after a fork(). (The MADV_WIPEONFORK setting remains in place for those address ranges in the child.)
* The termination signal of the child is always SIGCHLD (see clone(2)).
* The port access permission bits set by ioperm(2) are not inherited by the child; the child must turn on any bits that it requires using ioperm(2).
* After a fork() in a multithreaded program, the child can safely call only async-signal-safe functions (see signal-safety(7)) until such time as it calls execve(2).
* The child inherits copies of the parent's set of open file descriptors. Each file descriptor in the child refers to the same open file description (see open(2)) as the corresponding file descriptor in the parent. This means that the two file descriptors share open file status flags, file offset, and signal-driven I/O attributes (see the description of F_SETOWN and F_SETSIG in fcntl(2)).
* The child inherits copies of the parent's set of open message queue descriptors (see mq_overview(7)). Each file descriptor in the child refers to the same open message queue
description as the corresponding file descriptor in the parent. This means that the two file descriptors share the same flags (mq_flags).
* The child inherits copies of the parent's set of open directory streams (see opendir(3)). POSIX.1 says that the corresponding directory streams in the parent and child may share
the directory stream positioning; on Linux/glibc they do not.
RETURN VALUE
On success, the PID of the child process is returned in the parent, and 0 is returned in the child. On failure, -1 is returned in the parent, no child process is created, and errno is
set appropriately.
ERRORS
EAGAIN A system-imposed limit on the number of threads was encountered. There are a number of limits that may trigger this error:
* the RLIMIT_NPROC soft resource limit (set via setrlimit(2)), which limits the number of processes and threads for a real user ID, was reached;
* the kernel's system-wide limit on the number of processes and threads, /proc/sys/kernel/threads-max, was reached (see proc(5));
* the maximum number of PIDs, /proc/sys/kernel/pid_max, was reached (see proc(5)); or
* the PID limit (pids.max) imposed by the cgroup "process number" (PIDs) controller was reached.
EAGAIN The caller is operating under the SCHED_DEADLINE scheduling policy and does not have the reset-on-fork flag set. See sched(7).
ENOMEM fork() failed to allocate the necessary kernel structures because memory is tight.
ENOMEM An attempt was made to create a child process in a PID namespace whose "init" process has terminated. See pid_namespaces(7).
ENOSYS fork() is not supported on this platform (for example, hardware without a Memory-Management Unit).
ERESTARTNOINTR (since Linux 2.6.17)
System call was interrupted by a signal and will be restarted. (This can be seen only during a trace.)
fork()通过复制调用进程来创建一个新进程。新进程称为子进程。调用进程称为父进程。
子进程和父进程在不同的内存空间中运行。在执行fork()时,两个内存空间具有相同的内容。内存写入、文件映射(mmap(2))和
其中一个进程执行的取消映射(munmap(2))不会影响另一个进程。
子进程与父进程完全相同,但以下几点除外:
*子进程有自己唯一的进程ID,并且此PID与任何现有进程组(setpgid(2))或会话的ID不匹配。
*子进程的父进程ID与父进程ID相同。
*子级不继承其父级的内存锁(mlock(2)、mlockall(2))。
*子进程中的进程资源利用率(getrusage(2))和CPU时间计数器(times(2))重置为零。
*子级的挂起信号集最初为空(sigpending(2))。
*子级不从其父级继承信号量调整(semop(2))。
*子进程不从其父进程继承进程关联的记录锁(fcntl(2))。(另一方面,它继承fcntl(2)open file description锁和flock(2)锁
从它的父级。)
*子级不从其父级继承计时器(setitimer(2)、alarm(2)、timeru create(2))。
*子级不从其父级继承未完成的异步I/O操作(aio U read(3)、aio U write(3)),也不从其父级继承任何异步I/O上下文
(参见ioèU设置(2))。
前面列表中的进程属性都是在POSIX.1中指定的。父进程和子进程在以下特定于Linux的进程属性方面也有所不同:
*子目录不从其父目录继承目录更改通知(dnotify)(请参阅fcntl(2)中F_NOTIFY的描述)。
*prctl(2)PRu SETu PDEATHSIG设置被重置,这样子级在其父级终止时不会收到信号。
*默认计时器时差值设置为父级的当前计时器时差值。参见prctl(2)中PRu SETu TIMERSLACK的描述。
*使用madvise(2)MADVu DONTFORK标志标记的内存映射不会跨fork()继承。
*用madvise(2)MADVu WIPEONFORK标志标记的地址范围中的内存在fork()之后的子级中归零。(MADVèu WIPEONFORK设置保持不变,以便
子对象中的地址范围。)
*子代的终止信号总是SIGCHLD(参见克隆(2))。
*ioperm(2)设置的端口访问权限位不会被子级继承;子级必须打开使用ioperm(2)所需的任何位。
*使用madvise(2)MADVu DONTFORK标志标记的内存映射不会跨fork()继承。
*用madvise(2)MADVu WIPEONFORK标志标记的地址范围中的内存在fork()之后的子级中归零。(MADVèu WIPEONFORK设置保持不变,以便
子对象中的地址范围。)
*子代的终止信号总是SIGCHLD(参见克隆(2))。
*ioperm(2)设置的端口访问权限位不会被子级继承;子级必须打开使用ioperm(2)所需的任何位。
请注意以下要点:
*子进程是用一个名为fork()的线程创建的。父级的整个虚拟地址空间都复制到子级中,包括
mutexe、条件变量和其他pthreads对象;pthread_atfork(3)的使用可能有助于处理可能导致的问题。
*在多线程程序中使用fork()之后,子进程只能安全地调用异步信号安全函数(请参见信号安全(7)),直到调用execve(2)。
*子级继承父级的一组打开的文件描述符的副本。子级中的每个文件描述符引用与相应的
正在父级中删除文件描述符。这意味着这两个文件描述符共享打开的文件状态标志、文件偏移量和信号驱动的I/O属性(请参见
fcntl(2)中的F_SETOWN和F_setig。
*子级继承父级的一组打开的消息队列描述符的副本(请参见mqu概述(7))。子级中的每个文件描述符都引用相同的打开消息队列
作为父级中相应的文件描述符。这意味着两个文件描述符共享相同的标志(mqu标志)。
*子级继承父级的一组开放目录流的副本(请参见opendir(3))。POSIX.1表示父级和子级中相应的目录流可以共享
目录流定位;在Linux/glibc上它们没有。
返回值
成功时,子进程的PID在父进程中返回,0在子进程中返回。如果失败,则在父进程中返回-1,不创建子进程,并返回errno
适当设置。
错误
EAGAIN遇到系统对线程数施加的限制。可能触发此错误的限制有很多:
*达到了限制实际用户ID的进程和线程数的RLIMITu NPROC软资源限制(通过setrlimit(2)设置);
*达到了内核对进程和线程数量的系统范围限制,/proc/sys/kernel/threads max(参见proc(5));
*已达到最大pid数/proc/sys/kernel/pidèmax(请参阅proc(5));或
*PID限制(最大pids)由cgroup施加的“进程号”(PIDs)控制器已到达。
EAGAIN调用者在SCHEDu DEADLINE调度策略下操作,并且没有设置reset on fork标志。见附表(7)。
由于内存紧张,ENOMEM fork()无法分配必要的内核结构。
ENOMEM试图在“init”进程已终止的PID命名空间中创建子进程。请参见pid U名称空间(7)。
ENOSYS fork()在此平台上不受支持(例如,没有内存管理单元的硬件)。
eRestartPointr(自Linux 2.6.17起)
系统调用被信号中断,将重新启动。(这只能在跟踪过程中看到。)
作为初学者的角度看来,fork基本就是将原来的程序拷贝了一个镜像,然后去继续运行,这个镜像的运行中有一些值是会被拷贝的,例如变量等会被拷贝下来,有一些是子进程不会继承父进程的,例如
2. 示例代码
#include <unistd.h>
#include <stdio.h>
#include <sys/prctl.h> // 用来设置进程的名字
#include <sched.h>// 调度相关 这个例子用不到
int main(void)
{
int pid = fork();
int cnt = 100;
if( pid == 0 ) // 子进程
{
prctl(PR_SET_NAME, "child_name"); // 设置子进程的名字
while(cnt -- ){
printf("this is subprocessn");
usleep(1000000);
}
}
else
{
prctl(PR_SET_NAME, "father_name"); // 设置父进程的名字
printf("pid = %dn",pid); // 这个是子进程的ID
while(cnt --){
printf("this is main processn");
usleep(1000000);
}
}
}
#输出信息
root@jeason:~/work/ipc/ipcs# g++ fork.cpp
root@jeason:~/work/ipc/ipcs# ./a.out
pid = 515
this is main process
this is subprocess
this is main process
this is subprocess
this is main process
this is subprocess
#....
# 观察创建的进程的信息
root@jeason:~/work/ipc/mutex# ps -eo state,uid,pid,ppid,rtprio,time,comm
S UID PID PPID RTPRIO TIME COMMAND
S 0 1 0 - 00:00:11 systemd
S 0 2 0 - 00:00:00 kthreadd
#...
I 0 508 2 - 00:00:00 ttm_swap
S 0 514 24727 - 00:00:00 father_name # 被调用起来的父进程
S 0 515 514 - 00:00:00 child_name # 子进程
S 0 531 24715 - 00:00:00 bash
S 0 532 531 - 00:00:00 sleep
#...
S 0 24715 865 - 00:00:04 sshd
S 0 24725 865 - 00:00:00 sshd
S 0 24727 24715 - 00:00:00 bash # 调用运行a.out的bash
最后
以上就是大力水壶为你收集整理的嵌入式linux学习笔记---使用fork创建子进程的全部内容,希望文章能够帮你解决嵌入式linux学习笔记---使用fork创建子进程所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复