fork的使用
fork用于创建子进程,而exec可以替换子进程执行的代码。
一个简单的例子:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
int main(){
pid_t pid;
int ret = 1;
int status;
pid = fork();
if (pid == -1){
// pid == -1 means error occured
printf("can't fork, error occured\n");
exit(EXIT_FAILURE);
}
else if (pid == 0){
// pid == 0 means child process created
// getpid() returns process id of calling process
printf("child process, pid = %u\n",getpid());
// the argv list first argument should point to
// filename associated with file being executed
// the array pointer must be terminated by NULL
// pointer
char * argv_list[] = {"ls","-lart","/home",NULL};
// the execv() only return if error occured.
// The return value is -1
execv("ls",argv_list);
exit(0);
}
else{
// a positive number is returned for the pid of
// parent process
// getppid() returns process id of parent of
// calling process
printf("parent process, pid = %u\n",getppid());
// the parent process calls waitpid() on the child
// waitpid() system call suspends execution of
// calling process until a child specified by pid
// argument has changed state
// see wait() man page for all the flags or options
// used here
if (waitpid(pid, &status, 0) > 0) {
if (WIFEXITED(status) && !WEXITSTATUS(status))
printf("program execution successfull\n");
else if (WIFEXITED(status) && WEXITSTATUS(status)) {
if (WEXITSTATUS(status) == 127) {
// execv failed
printf("execv failed\n");
}
else
printf("program terminated normally,"
" but returned a non-zero status\n");
}
else
printf("program didn't terminate normally\n");
}
else {
// waitpid() failed
printf("waitpid() failed\n");
}
exit(0);
}
return 0;
}
Namespace相关
Linux kernel now tracks process’s PIDs using upid structure instead of a single pid value. The upid structure tells about the pid and the namespaces where that pid is valid.
struct upid {
int nr; /* moved from struct pid */
struct pid_namespace *ns; /* the namespace this value
* is visible in
*/
...
};
struct pid {
atomic_t count;
struct hlist_head tasks[PIDTYPE_MAX];
struct rcu_head rcu;
int level; /* the number of upids */
struct upid numbers[0];
};
Types of namespaces
Linux provides following namespaces:
- cgroup:This isolates Cgroup root directory(CLONE_NEWCGROUP)
- IPC: isolates System V IPC, POSIX message queues(CLONE_NEWIPC)
- Network: isolates Network devices, ports etc(CLONE_NEWNET)
- Mount: isolates mountpoints(CLONE_NEWNS)
- PID: isolated process IDs(CLONE_NEWPID)
- User : isolates User and group IDs(CLONE_NEWUSER)
- UTS: isolates Hostname and NIS domain name(CLONE_NEWUTS)
As part of namespace management, Linux provides below APIs:
clone()- plain old clone() creates a new process. If we pass one or more CLONE_NEW* flags to clone(),then new namespaces are created for each
flag, and the child process is made a member of those
namespaces.
setns()- allows a process to join an existing namespace. The namspace is specified by a file descriptor reference to one of proc/[pid]/nsfiles.
unshare()- moves calling process to a new namespace created according to CLONE_NEW* arguments. More than one such flags can be specified.
Note:For PID namespace, clone() MUST be called as that can be created only while a new process is created. Spawned process as a result of clone() has pid 1. For PID namespace , unshare() wont be useful.
参考文献
- https://josedonizetti.github.io/cni/namespace/bridge/linux/network/2017/10/22/container-networking-bridge.html
- https://itnext.io/chroot-cgroups-and-namespaces-an-overview-37124d995e3d