进程管理
创建并运行新进程
fork(), execl(), exit(), getpid(), getppid()
fork()
: 当前进程(父进程)创建一个新的进程(子进程),创建后的父、子进程继续执行fork()调用点后面的程序,当然也可以使用比如execl()
来替换后续要执行的程序。
execl()
: 替换当前进程后续要执行的程序(从execl()调用点后续的程序被替换,也就是说原来在execl()调用点后面的程序不再执行,而去执行execl参数中指定的程序)。
exit()
: 终止一个进程,将进程占用的所有资源(内存,文件描述符)交还内核,由其进行再次分配。
getpid()
: 获取当前进程PID
getppid()
: 获取父进程PID
/*
* 获取当前进程pid: int getpid();
* 获取父进程pid: int getppid();
* 执行新的程序: int execl(char *path, char *arg, ...);
* 创建子进程: int fork();
* 终止进程: void exit(int status);
*/
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main(void) {
long pid;
pid = fork();
/* fork执行错误 */
if(pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
/* parent */
if(pid > 0) {
int pret;
printf("pid: %d, child_pid: %d\n", getpid(), pid);
pret = execl("/usr/bin/sleep","sleep", "100", NULL);
if(pret == -1) {
perror("parent execl");
exit(EXIT_FAILURE);
}
}
/* child */
if(pid == 0) {
int cret;
printf("pid: %d, parent_pid: %d\n", getpid(), getppid());
cret = execl("/usr/bin/sleep","sleep", "100", NULL);
if(cret == -1) {
perror("child execl");
exit(EXIT_FAILURE);
}
}
}
输出
pid: 22190, child_pid: 22191
pid: 22191, parent_pid: 22190
execl, execlp, execle, execv, execvp
/*
* int execl(char *path, char *arg, ...);
* int execlp(char *file, char *arg, ...);
* int execle(char *path, char *arg, ...);
* int execv(char *path, char *argv[]);
* int execvp(char *file, char *argv[]);
*/
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main(void) {
int pret;
char *args[] = {"sleep", "100", NULL};
pret = execvp("sleep",args);
if(pret == -1) {
perror("parent execl");
exit(EXIT_FAILURE);
}
}
wait(), waitpid() 等待子进程退出
wait(), waitpid()用来让父进程获取子进程退出的信息
wait(): 获取任意一个子进程退出信息
waitpid(): 获取特定子进程退出信息
wait
/*
* int wait(int *status);
*/
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void) {
int status;
int pid, wpid;
pid = fork();
if(pid == 0) {
execl("/usr/bin/sleep", "sleep", "10", NULL);
}
if(pid > 0) {
printf("pid: %d, child_pid: %d\n", pid, getpid());
wpid = wait(&status);
printf("wpid = %d\n", wpid);
if(WIFEXITED(status)) {
printf("Normal exit, status = %d\n", WEXITSTATUS(status));
}
if(WIFSIGNALED(status)) {
printf("killed by singnal = %d\n", WTERMSIG(status));
}
if(WIFSTOPPED(status)) {
printf("stopped by singnal = %d\n", WSTOPSIG(status));
}
}
return 0;
}
pid_t waitpid(pid_t pid, int wstatus, int options);
/*
* int waitpid(int pid, int *status, int options);
*/
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void) {
int pid;
printf("start process\n");
pid = fork();
if(pid == 0) {
printf("child process ...\n");
execl("/usr/bin/bash", "bash", NULL);
return 1;
}
waitpid(pid, NULL, 0);
printf("process stopped\n");
}
clone
clone()
与fork()
类似,都是用来创建新进程,不同之处在于:
forck()
创建的子进程仍然继续运行调用点后面的程序
clone()
创建的子进程不再执行调用点后面的程序,转而去执行参数中func指定的函数
int clone(int (*func)(void *), void *child_stack, int flags)
示例
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>
#define STACK_SIZE (1024 * 1024)
static char child_stack[STACK_SIZE];
int child_process() {
printf("start child process ...\n");
execl("/usr/bin/sleep", "sleep", "100", NULL);
return 1;
}
int main(void) {
int pid;
printf("start process\n");
pid = clone(child_process, child_stack + STACK_SIZE, SIGCHLD, NULL);
waitpid(pid, NULL, 0);
printf("process stopped\n");
}
setns(), unshare()
#define _GNU_SOURCE
#include <sched.h>
setns(int fd, int nstype);
将当前进程加入到指定的namespace中,fd为进程描述符或路径
#define _GNU_SOURCE
#include <sched.h>
unshare(int flags)
退出当前Namespace,并加入新创建的Namespace
flags:
CLONE_FILES
CLONE_FS
CLONE_NEWCGROUP
CLONE_NEWIPC
CLONE_NEWPID
CLONE_NEWNET
CLONE_NEWNS
CLONE_NEWUSER
CLONE_NEWUTS
会话和进程组
用户登录:
a. bash进程创建会话和进程组,bing分配一个终端与该会话建立连接
终端产生的信号:
a. 发送信号给前台进程组中的所有进程
网络异常断开:
a. 发送SIGHUP信号给前台进程,分配给会话的tty仍然保持连接
退出登录
a. 发送SIGHUP信号给前台进程
b. 终端与session断开连接
/*
* 创建新会话:int setsid();
*/
#include <unistd.h>
int main(void) {
int sid;
sid = setsid();
if(sid == -1) {
perror("setsid");
}
execl("/usr/bin/sleep", "sleep", "100", NULL);
}
$ ps -o tty,sid,pgid,ppid,pid,args -eH |less
TT SID PGID PPID PID COMMAND
pts/0 45181 45181 45180 45181 -bash
pts/0 45181 49838 45181 49838 sudo ./a.out
? 49840 49840 49838 49840 sleep 100
/*
* 创建新进程组:int setpgid(int pid, int pgid);
将pid的进程组设置成pgid, pid为0时设置当前进程
* 获取新进程组: getpgid(int pid)
*/
#include <unistd.h>
int main(void) {
int pgid;
int pid;
pgid = getpgid(0);
pid = fork();
if(pid == 0) {
setpgid(0, pgid);
execl("/usr/bin/sleep", "sleep", "100", NULL);
}
if(pid > 0) {
execl("/usr/bin/ping", "ping", "www.baidu.com", NULL);
}
return 0;
}
/tmp/tmplbo7xu6h.c: In function ‘main’:
/tmp/tmplbo7xu6h.c:12:5: warning: implicit declaration of function ‘getpgid’ [-Wimplicit-function-declaration]
pgid = getpgid(0);
^
PING www.a.shifen.com (220.181.38.150) 56(84) bytes of data.
64 bytes from 220.181.38.150 (220.181.38.150): icmp_seq=1 ttl=52 time=4.84 ms
64 bytes from 220.181.38.150 (220.181.38.150): icmp_seq=2 ttl=52 time=4.49 ms
64 bytes from 220.181.38.150 (220.181.38.150): icmp_seq=3 ttl=52 time=4.51 ms
64 bytes from 220.181.38.150 (220.181.38.150): icmp_seq=4 ttl=52 time=4.62 ms
64 bytes from 220.181.38.150 (220.181.38.150): icmp_seq=5 ttl=52 time=4.43 ms
64 bytes from 220.181.38.150 (220.181.38.150): icmp_seq=6 ttl=52 time=4.46 ms
64 bytes from 220.181.38.150 (220.181.38.150): icmp_seq=7 ttl=52 time=4.52 ms
64 bytes from 220.181.38.150 (220.181.38.150): icmp_seq=8 ttl=52 time=4.49 ms
64 bytes from 220.181.38.150 (220.181.38.150): icmp_seq=9 ttl=52 time=4.51 ms
守护进程
成为守护的步骤:
a. 调用fork()创建新进程
b. 结束父进程,使新进程脱离父进程,使init成为其父进程
c. 调用setsid()创建新的会话,脱离原来的会话
d. 调用chdir()将其工作目录修改为"/",脱离原来的工作目录
e. 关闭所有文件描述符
f. 打开文件描述符0, 1, 2, 并重定向到/dev/null
/*
* 成为守护进程的过程
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/fs.h>
int main(void) {
int pid, i;
pid = fork();
if(pid == -1)
return -1;
else if(pid != 0)
exit(EXIT_SUCCESS);
/* create new session */
if(setsid() == -1)
return -1;
/* set the working directory to the root directory */
if(chdir("/") == -1)
return -1;
/* close all open files*/
for(i=0; i<NR_OPEN; i++)
close(i);
/* redirect fd's 0, 1, 2, to /dve/null */
open("/dev/null", O_RDWR); /* stdin */
dup(0); /* stdout */
dup(0); /* stderror */
/* do its daemon thing */
return 0;
}
/*
* 直接调用 int daemon(int nochdir, int noclose); 创建守护进程,避免繁琐的创建过程
nochdir为0时,修改工作目录为根目录
noclose为0时,关闭所有文件描述符
*/
进程调度
普通进程与实时进程的调度策略完全不同, 优先级系统也不同:
a. 普通进程的调度策略是雨露均沾,不会因为优先级低而被饿死,优先级高低影响的是时间片的长短。优先级采用的是动态优先级nice[-20, 19]
b. 普通进程:拥有root权限的可以提高自己优先级,即降低nice值,非root用户只能降低自己优先级。
c. 普通进程默认nice值为0
d. 实时进程采用的是霸占模式,只有高优先级的进程运行完才轮得到低优先级的进程,当高优先级的进程进入就绪态,将毫不留情的抢占低优先级的CPU时间。
普通进程优先级
/*
* 普通进程修改nice值: int nice(int inc);
*/
#include <unistd.h>
#include <stdio.h>
int main(void) {
int ret;
errno = 0;
ret = nice(10);
if(ret == -1 && errno != 0)
perror("nice");
else
printf("nice value is %d\n", ret);
}
/*
* 修改进程、进程组、用户优先级: int setpriority(int which, int who, int prio);
which:
PRIO_PROCESS
PRIO_PGRP
PRIO_USER
who:
0时:当前进程、进程组、用户
* 获取优先级: int getpriority(int which, int who);
*/
#include <stdio.h>
#include <sys/time.h>
#include <sys/resource.h>
int main() {
int nice, ret;
nice = getpriority(PRIO_PGRP, 0);
printf("nice value is %d\n", nice);
ret = setpriority(PRIO_PGRP, 0, 10);
if(ret == -1)
perror("setpriority");
nice = getpriority(PRIO_PGRP, 0);
printf("nice value is %d\n", nice);
}
/*
* 处理器亲和力
* void CPU_SET(unsigned long cpu, cpu_set_t *set); 将set结构体中的#cpu置为1
* void CPU_CLR(unsigned long cpu, cpu_set_t *set); 将set结构体中的#cpu置为0
* int CPU_ISSET(unsigned long cpu, cpu_set_t *set); 判断#cpu是否为1, 是返回1, 否0
* void CPU_ZERO(cpu_set_t *set); 将set结构体中的cpu全部置为0
* int sched_setaffinity(pid_t pid, size_t setsize, const cpu_set_t *set) 获取进程pid可绑定的cpu存储于set中
* int sched_setaffinity(pid_t pid, size_t setsize, cpu_set_t *set) 将set中的cpu与进程绑定
*/
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
int main(void) {
cpu_set_t set;
int ret, i;
CPU_ZERO(&set);
ret = sched_getaffinity(0, sizeof(cpu_set_t), &set);
if(ret == -1)
perror("sched_getaffinity");
for(i=0; i<CPU_SETSIZE; i++) {
int cpu;
cpu = CPU_ISSET(i, &set);
printf("cpu=%i is %s\n", i, cpu?"set":"unset");
}
/* 设定亲和力*/
CPU_ZERO(&set);
CPU_SET(0, &set);
CPU_CLR(1, &set);
ret = sched_setaffinity(0, sizeof(cpu_set_t), &set);
if(ret == -1)
perror("sched_setaffinity");
for(i=0;i<CPU_SETSIZE; i++){
int cpu;
cpu = CPU_ISSET(i, &set);
printf("cpu=%i is %s\n", i, cpu?"set":"unset");
}
}
实时进程
/*
* int sched_getscheduler(int pid); 获取pid进程的调度策略
*/
#include <sched.h>
#include <stdio.h>
int main() {
int police;
/* 当前进程调度策略 */
police = sched_getscheduler(0);
switch(police) {
case SCHED_OTHER:
printf("Policy is normal\n");
break;
case SCHED_FIFO:
printf("Policy is FIFO\n");
break;
case SCHED_RR:
printf("Policy is Round-Robin\n");
break;
case -1:
perror("sched_getscheduler");
break;
default:
printf("Unknown Policy\n");
}
}
Policy is normal
/*
* int sched_setscheduler(int pid, int policy, const struct sched_param *sp);
* 修改pid进程的调度策略
*/
#include <sched.h>
#include <stdio.h>
int main() {
int police, ret;
struct sched_param sp;
/* 静态优先级 */
sp.sched_priority = 2;
/* 设定当前进程调度策略为 Round Robin */
ret = sched_setscheduler(0, SCHED_RR, &sp);
if(ret == -1) {
perror("sched_setscheduler");
return 1;
}
/* 当前进程调度策略 */
police = sched_getscheduler(0);
switch(police) {
case SCHED_OTHER:
printf("Policy is normal\n");
break;
case SCHED_FIFO:
printf("Policy is FIFO\n");
break;
case SCHED_RR:
printf("Policy is Round-Robin\n");
break;
case -1:
perror("sched_getscheduler");
break;
default:
printf("Unknown Policy\n");
}
}
sched_setscheduler: Operation not permitted
[C kernel] Executable exited with code 1
/*
* int shced_getparam(int pid, struct sched_param *sp);
* 获取pid进程的调度参数
* int shced_setparam(int pid, const struct sched_param *sp);
* 设定pid进程的调度参数
*/
#include <sched.h>
#include <stdio.h>
int main() {
int ret;
struct sched_param sp;
/* 获取当前进程调度参数 */
ret = sched_getparam(0, &sp);
if(ret == -1) {
perror("sched_getparam");
return 1;
}
printf("Priority is: %d", sp.sched_priority);
}
Priority is: 0
/*
* int sched_get_priority_min(int policy);
* int sched_get_priority_max(int policy);
* 获取调度策略的优先级范围
*/
#include <sched.h>
#include <stdio.h>
int main(void) {
int priority_range[2];
/* 获取Round-Robin调度策略的最小优先级 */
priority_range[0] = sched_get_priority_min(SCHED_RR);
if(priority_range[0] == -1) {
perror("sched_get_priority_min");
return 1;
}
/* 获取Round-Robin调度策略的最大优先级 */
priority_range[1] = sched_get_priority_max(SCHED_RR);
if(priority_range[1] == -1) {
perror("sched_get_priority_max");
return 1;
}
printf("SCHED_RR priority range is: %d - %d", priority_range[0], priority_range[1]);
}
SCHED_RR priority range is: 1 - 99
进程资源限制
Linux内核对进程所能获取的资源进行了限制,比如打开文件数目,内存页数,未处理的信号等。
/*
* int getrlimit(int resource, struct rlimit *rlim);
* 获取资源限制
*/
#include <sys/time.h>
#include <sys/resource.h>
#include <stdio.h>
int main() {
struct rlimit rlim;
int ret;
/* 获取当前进程NOFILE限制 */
ret = getrlimit(RLIMIT_NOFILE, &rlim);
if(ret == -1) {
perror("getrlimit");
return 1;
}
printf("RLIMIT_NOFILE limits: soft=%d hard=%d\n",
rlim.rlim_cur, rlim.rlim_max);
}
RLIMIT_NOFILE limits: soft=65535 hard=65535
/*
* int setrlimit(int resource, const struct rlimit *rlim);
* 设定资源限制
*/
#include <sys/time.h>
#include <sys/resource.h>
#include <stdio.h>
int main() {
struct rlimit rlim;
int ret;
/* 设定core文件最大为32MB */
rlim.rlim_cur = 32 * 1024 * 1024;
rlim.rlim_max = RLIM_INFINITY;
ret = setrlimit(RLIMIT_CORE, &rlim);
if(ret == -1) {
perror("setrlimit");
return 1;
}
}