10进程控制1
Posted gd-luojialin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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"));
以上是关于10进程控制1的主要内容,如果未能解决你的问题,请参考以下文章