操作系统可以运行多个程序,那他是如何运行的?实际上,CPU的执行是很快的,而待运行的程序很多,那么为了让操作系统运行多个程序,CPU会把它的执行时间划分成很多段,比如每一段是0.1秒,那么就可以这样A程序运行0.1秒,然后B程序运行0.1,然后C程序运行0.2秒,因为这个切换很快,所以我们感觉程序是同时运行的。
创建进程用法举例:
#include
#include
#include
int main(){
int pid = 0;// 父子进程不共享局部变量
// pid_t fork(void);
pid_t fpid = fork(); // fpid表示fork函数返回的值
if(fpid < 0){
printf("创建子进程失败\n");
return fpid;
}else if(fpid == 0){
// 创建成功后子进程中fpid == 0
pid = getpid();
printf("子进程,子进程pid = %d\n",getpid());
}else if(fpid > 0){
// 创建成功后父进程的fpid 为子进程的pid
pid = getpid();
printf("父进程,父进程pid = %d\n",getpid());
printf("父进程,子进程pid = %d\n",getpid());
}
// 再创建3个子进程用于观察
for(int i = 3 ; i > 0 && fpid > 0 ; i--){
fpid = fork();
}
printf("pid = %d\n",pid);
while (1);
return 0;
}
调用fork函数后,会创建一个子进程,并且父子两个进程都从fork处执行,fork函数有两个返回值,对于父进程会返回子进程的pid,此时pid会大于0,对于子进程来说,pid会等于0。
局部变量:写时复制,读时共享
exit - 终止正在执行的进程
#include
#include
#include
#include
int main()
{
int pid = 0;
// pid_t fork(void);
pid_t fpid = fork();
if (fpid < 0)
{
printf("创建子进程失败\n");
return fpid;
}
else if (fpid == 0)
{
printf("我是子进程,马上就要exit了\n");
exit(-1);
printf("子进程还活着\n");
}
else if (fpid > 0)
{
printf("父进程还活着\n");
}
while (1);
return 0;
}
这里子进程已经退出,但是父进程没有执行完毕,导致子进程没有回收变成僵尸进程
若要用多进程实现高并发那么自己linxu系统cpu有多少核,就产生多少个进程多个核可以并发执行只有多个进程在不同的核上运行才可以充分利用多核系统的并发处理能力
比如:cpu为四核,那么主进程就产生4个子进程,让四个子进程分别在不同的核上运行,
4个子进程在工作的时候,把任务放在一个共享内存中(通过内存映射,使多个进程可以访问一个内存),哪个任务做完了,就接着拿任务给每个子进程的负载均衡
职责明确:父进程管理生死,子进程工作
查看cpu核数
cat /proc/cpuinfo
#define _GNU_SOURCE
#include
#include
#include
#include
#include
typedef void (*spawn_proc_pt)(void *data);
static void worker_process_cycle(void *data);
static void start_worker_processes(int n);
pid_t spawn_process(spawn_proc_pt proc, void *data, char *name);
int main(int argc, char **argv)
{
// 子进程用于工作,父进程用于管理子进程
// 开启工作进程,创建4个子进程
start_worker_processes(4);
// 管理子进程
wait(NULL);
}
// 开启工作进程
void start_worker_processes(int n)
{
int i = 0;
for (i = n - 1; i >= 0; i--)
{
// 创建子进程
spawn_process(worker_process_cycle, (void *)(intptr_t)i, "worker process");
}
}
// 创建子进程
// spawn_proc_pt 类型的函数:void worker_process_cycle(void *data)
pid_t spawn_process(spawn_proc_pt proc, void *data, char *name)
{
pid_t pid;
pid = fork(); // 创建子进程
switch (pid)
{
case -1:
fprintf(stderr, "fork() failed while spawning \"%s\"\n", name);
return -1;
case 0:
proc(data); // 成功创建子进程后让子进程去工作
return 0;
default:
break;
}
printf("start %s %ld\n", name, (long int)pid);
return pid;
}
// 给进程安排工作
void worker_process_cycle(void *data)
{
// data 其实是一个int*类型
int worker = (intptr_t)data;
// 初始化
worker_process_init(worker);
// 干活
for (;;)
{
sleep(10);
printf("pid %ld ,doing ...\n", (long int)getpid());
}
}
// 初始化进程
void worker_process_init(int worker)
{
cpu_set_t cpu_affinity;
// worker = 2;
// 多核高并发处理 4core 0 - 0 core 1 - 1 2 -2 3 -3
CPU_ZERO(&cpu_affinity); // 结构体清零
// CPU_SETSIZE:1024 支持cpu最大的数量
CPU_SET(worker % CPU_SETSIZE, &cpu_affinity); // 0 1 2 3 设置核
/*
在多CPU系统中,通过sched_setaffinity ()可以设置进程的CPU亲和力,
使进程绑定在某一个或几个CPU上运行,避免在CPU之间来回切换,从而提高该进程的实时性能。
*/
if (sched_setaffinity(0, sizeof(cpu_set_t), &cpu_affinity) == -1)
{
fprintf(stderr, "sched_setaffinity() failed\n");
}
}
代码中函数指针不太理解的看看下方代码:
// typedef 返回类型(*新类型)(参数表)
typedef char (*PTRFUN)(int);
PTRFUN pFun;
char glFun(int a){ return;}
void main()
{
pFun = glFun;
(*pFun)(2); //调用函数
}
查看进程在cpu的核上执行的命令: ps -eLo ruser,pid,lwp,psr,args
一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程所收养,并由init进程对它们完成状态收集工作。
想想我们如何模仿一个孤儿进程? 答案是: kill 父进程!
#include
#include
#include
int main(){
int fpid = 1;// 父子进程不共享局部变量
for(int i = 5 ; i > 0 && fpid > 0 ; i--){
fpid = fork();
}
if(fpid < 0){
printf("创建子进程失败\n");
return fpid;
}else if(fpid == 0){
while(1);
}else if(fpid > 0){
while(1);
}
return 0;
}
把孤儿进程做成守护进程
不与任何终端关联的进程,通常情况下守护进程在系统启动时就在运行,它们以root用户或者其他特殊用户(apache和postfix)运行,并能处理一些系统级的任务。守护进程脱离于终端,是为了避免进程在执行过程中的信息在任何终端上显示,并且进程也不会被任何终端所产生的终端信息所打断(比如关闭终端等)。那如何成为一个守护进程呢? 步骤如下:
#include
#include
#include
#include
#include
#include
// 一般传入 0 , 0
int daemon(int nochdir, int noclose)
{
int fd = 0;
switch (fork())
{
case -1:
return (-1);
case 0:
break;
default:
_exit(0); // 父进程自杀
}
if (setsid() == -1) // 创建会画
return (-1);
if (!nochdir) // 将当前目录改成根目录
(void)chdir("/");
if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1)
{
// 重定向 标准输入 标准输出 标准出错
(void)dup2(fd, STDIN_FILENO);
(void)dup2(fd, STDOUT_FILENO);
(void)dup2(fd, STDERR_FILENO);
if (fd > 2)
(void)close(fd);
}
return (0);
}
int main()
{
// 创建子进程,并且把父进程杀死
daemon(0,0);
printf("hello\n");
while(1);// 处理事务
return 0;
}
父进程自杀后,守护进程 init 接管 , 一直在后台进行运行
一个进程使用fork创建子进程,如果子进程退出(exit),而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
1.怎么查看僵尸进程:
利用命令ps,可以看到有标记为的进程就是僵尸进程。
#include
#include
#include
#include
int main(){
pid_t fpid = fork();
if(fpid < 0){
printf("创建子进程失败\n");
return fpid;
}else if(fpid == 0){
exit(-1);
}else if(fpid > 0){
while(1);
}
return 0;
}
这里可以用 wait 和 waitpid 函数回收子进程
2.怎样来清除僵尸进程: