10进程控制1

概念

程序:一个保存在磁盘中的文件,规定运行时要执行的代码和要完成的动作。

进程:把程序加载为内存中一段数据,程序的执行过程,具有产生,发展和消亡的过程

线程:unix的最小调度单位,一个进程可以有多个线程,共享进程ID,共享进程资源。

父子进程

进程采用树形结构管理,一个进程启动另一个进程时,被启动的进程就是子进程,原进程就是父进程。

fork():复制了父进程的数据,堆栈段,进程环境。另外,各个子进程拥有自己的进程环境

init进程: 所有进程的父进程

进程状态

运行态: 分配到了CPU

就绪态: 等待CPU

睡眠态: 不能获取CPU,直到某事发生,进入就绪态

进程属性函数:

进程标识符 pid:进程的唯一ID号

pid_t   getpid()       获取进程id

pid_t   getppid()     获取父进程id

pid_t   getpgrp()     获取进程组id

进程用户标识号

uid_t  getuid()         返回进程的用户ID  (uid: 用户ID)

uid_t  geteuid()       返回进程的有效用户ID  (该进程有哪个权限)

gid_t  getgid()          返回进程的组ID

gid_t  getegid()       返回进程的有效组ID

chmod u+s uid_demo    运行改程序的人,程序运行过程中,暂时拥有 文件属主 的用户权限

进程调用

创建新进程步骤

1、fork()  创建新进程,实现当前进程的复制

2.exec() 函数库修改创建的进程,创建进程的目的是为了运行新的程序,通过exec函数族修改子进程后,让子进程执行新的程序

创建新进程  pid_t fork()

<sys/types.h>

<unistd.h>

在父进程中,返回子进程PID, 子进程种返回0

例子

void testFork()

{

      pid_t pid=getpid();

      pid_t ppid=getppid();

      printf("Proc id:%d ",pid);

      printf("parent Proc:%d ",ppid);

      //创建进程

      pid_t pt=fork();

      //创建失败

      if(pt<0)

      {

           perror("fail fork");

      }

      //子进程

      else if(pt==0)

      {

            printf("this is  a  chinld produce!ID:%d ",getpid());

      }

      //父进程

      else

      {

           printf("this is  a  parent produce!ID:%d ",getpid());

      }

      printf("test")     

}

解析:

父子进程共享资源。当fork()使用后,后面的代码是父子进程都具备的。那么printf("test")将会输出两次。

fork()调用后,如果创建失败,即pt<0;

如果成功,首先启动父进程,即else

      {

           printf("this is  a  parent produce!ID:%d ",getpid());

      }

然后将会往下继续执行,printf("test")。。。。,直到exit()或_exit()或return或exec函数簇才会结束父进程。

当父进程结束后,才会执行子进程else if(pt==0)

      {

            printf("this is  a  chinld produce!ID:%d ",getpid());

      }

此时还会往下继续执行,因此资源是共享的。所以还会输出printf("test");

因此fork会使子进程复制父进程所有资源,有点多余,可使用vfork()。

vfork()

有时候,创建子进程为了运行其他程序,不需要复制父进程的资源。

vfork  共享父子进程资源。调用后,父进程阻塞,直到子进程调用exec()  或 _exit();

注意:不能使用 return 返回,或者 exit() 退出!

区别:

exit   _exit  return

_exit() : 直接退出,不关闭文件,不清理I/O

exit():   终止进程前,关闭文件,清理I/O

return:释放句柄,变量,弹出函数调用桟,回到上一级函数

exit 和 return 退出前,会调用 atexit() 注册的回调函数

c++中, return 会调用局部对象的析构函数,exit, _exit不会

建议使用fork(),

写时拷贝机制 (copy-on-write  COW)

fork()  函数需要复制父进程的资源 (变量)

刚开始创建时候,并不复制相关资源

如果只是读取值的话,访问相同的物理内存

一旦进行写操作,则先复制资源,然后再写

例子:

string str1="hello world";

string str2=str1;

printf("str1 adrr :%p",str1.c_str());

printf("str2 adrr :%p",str2.c_str());

此时发现str1,str2的地址是一样的。即使str1,str2是两个不同的变量。

修改:

str1[0]='H';

printf("str1 adrr :%p",str1.c_str());

printf("str2 adrr :%p",str2.c_str());

这时str1,str2的地址就会变成不一样了。

这就是写时拷贝机制,只有当被修改的时候才进程拷贝。

函数簇

exec 函数簇。执行另一个程序作为原进程的子进程

int execl(const char* path, const char *arg, ...)

int execlp(const char* file, const char *arg, ...)

int execle(const char* path, const char *arg, ..., char *const envp[]);

int execv(const char* path, const char **argv)

int execvp(const char* file, const char **argv)

int execve(const char* path, const char **argv, char *const envp[])

解析:

execl:入参是可变参数列表(一级指针), 以 NULL 结束

execv:  入参是参数数组(二级指针)

p 后缀:搜索环境变量 PATH,寻找可执行文件

e 后缀:显式传递环境变量

注意:exec函数调用成功后,系统会把新程序的地址空间替代调用进程的地址空间,并装入新程序内容。

例子:

void testExec()

{

      pid_t pid=fork();

      if(pid==0)

      {

           printf("this is a child:%d",getpid());

           //第一参数需要完整的路径

execl("/bin/ls","ls",”-l”,NULL);  

//或者以下两句替代

//char *buf[128]={“ls”,”-ls”};

//execl("/bin/ls",buf ,NULL);    

           //只需要名称

execlp("ls","ls",”-l”,NULL); 

           printf("end");

      }

      else

      {

           printf("this is a parent:%d",getpid());

      }

}

解析:

exec函数簇的主要作用是跳转。

立即跳转到输出ls的列表。

execl("/bin/ls","ls",”-l”,NULL);    

并且往下的代码将不会执行。

printf("end");不会输出。

当然,也可以跳转到自拟其他程序。

execlp("./mian2","./main2",NULL);   

环境变量

方式一:

env 命令: 显示当前用户所有环境变量

export 命令: 修改或者显示当前用户所有环境变量

expote |grep:查询指定环境变量

unset命令 :  删除某个环境变量  [  unset  AAA  ]

在终端直接使用命令:

设置环境变量:expoet  AA=”AAAAA”;

修改环境变量:expoet  AA=”BBBBBB”;

查询AA:expote |grep AA

删除AA:unset AA

方式二:

extern char **environ

UNIX系统中环境变量指针数组

例子:

//遍历出所有的环境变量

extern char ** environ;

char **p=environ;

      while(*p)

      {

           printf("%s ",*p);

           ++p;

      }

方式三:

头文件 <stdlib.h>

char* getenv(char *name)   

获取环境变量值,无则返回NULL

int    putenv(const char *expr) 

设置/修改环境变量, name=value, 

只传name则删除环境变量

例子:

      //设置环境变量

putenv("ABC=hello world");

      pid_t pid=fork();

      if(pid==0)

      {

           printf("this is child,ABC=%s ",getenv("ABC"));

      }

      else

      {

           printf("this is parent,ABC=%s ",getenv("ABC"));

           usleep(500);

      }

      //删除环境变量

      putenv("ABC");

      printf("ABC=%s ",getenv("ABC"));

原文地址:https://www.cnblogs.com/gd-luojialin/p/9215971.html