linux进程

本文深入解析Linux进程的概念,包括PID、进程状态、进程创建方法如fork和exec族函数,以及进程间通信的waitpid函数。文章详细介绍了fork函数的原理和exec族函数的功能区别,帮助读者理解进程的生命周期和管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

linux进程:
1.什么是PID?
进程号称为PID,例如PID为1的是init进程,他是所有进程的父进程或者祖宗进程。
2.两个函数?
getpid:得到当前进程的PID
getppid:得到当前进程的父进程的PID
3.进程是程序的执行过程,可分为三种状态:
执行态:这个进程正在执行,这个进程正在占用 CPU
就绪态:这个进程已经具备执行的所有条件,正在等待分配 CPU 的时间片,正在等待分配时间片
阻塞态: 这个进程还不具备执行的所有条件,正在等待某些条件满足,也可以说是在等待某些事件
在这里插入图片描述
其实也可以分为5种状态,还要加上:新建状态 和 终止状态
总结一下:
1.我们创建一个进程,也就是运行一个程序,会进入就绪状态
2.在就绪状态等待操作系统的调度,可以进入运行状态
3.在运行状态如果部分时间片使用完了或者其他高优先级的进程要占用CPU就会退回就绪状态。
如果在运行状态需要的某些资源正在被别的进程使用,当前进程就会等待这个资源被释放,从而进入阻塞状态。
如果进程执行完了,就会终止进程
4.当前进程在阻塞状态的时候,若等待的资源被释放了,就会进入就绪状态,等待被操作系统调度
注意:阻塞的进程是没法直接进入运行状态的,必须先进入就绪状态,然后等待被操作系统调用


如何创建新的进程? 使用fork函数
使用fork函数,fork函数一次调用,两次返回,什么意思呢?先看代码

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
int main(void)
{
pid_t ret = -1;
ret = fork();
if (ret < 0)
{
perror("");
return -1;
}
if (0 == ret)
{
printf(“child pid = %d ppid = %d\n”, getpid(), getppid());
while(1);
}
else
{
printf(“parent pid = %d ppid = %d\n”, getpid(), getppid());
while(1);
}
return 0;
}
调用了fork之后,这个进程就分裂成了两个进程,父进程和子进程。
在子进程中,ret=0,
在父进程中,ret是子进程的进程号。
所以由此可以判断该进程是父进程还是子进程,子进程几乎是父进程的复制品.
子进程的ppid和父进程的pid是相同的。

还有一个叫vfork的函数,它创建的进程不是父进程的复制品,它是通过允许父子进程可以
访问相同物理内存从而伪装了对进程地址空间的真实拷贝,当子进程需要改变内
存中数据时才真实拷贝父进程。这就是著名的“写操作时拷贝”技术
(copy-on-write)。


如何创建新的进程? 使用exec族函数
#include <unistd.h>
extern char **environ;
int execl(const char *path, const char arg, …
/
(char *) NULL */);
int execlp(const char *file, const char arg, …
/
(char *) NULL */);
int execle(const char *path, const char arg, …
/
, (char *) NULL, char * const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
char *const envp[]);

不是已经有了fork函数了吗?为什么还要使用exec族函数?
fork创建的进程几乎是父进程的复制品,那么exec函数族呢?
exec族函数可以根据pathname或者环境变量等找到可执行文件(2进制的可执行文件或者shell脚本),
将当前进程的.text、.data替换为所要加载的程序的.text、.data,然后让进程从新的.text第一条指令开始执行,但进程ID不变,可以理解为换核不换壳。
在实际的应用中,可以先用fork创建子进程,然后再在子进程中去使用exec族函数,去运行一个新的程序,但PID不会变

注意:exec函数族成功执行的话是没有返回的,失败的话返回-1,errno会被设置,我们用perro可以知道错误的原因

带l的:l 就是列举所有参数,list的意思
带p的:在PATH环境变量中去寻找这个程序,没找到的话返回-1
带v的:v 就是构造指针数组
带e的:e 就是 environment 环境的意思,即设置环境变量


execl函数举例:列举要去运行的程序的全路径
第一个参数:pathname
第二个参数:name
后面的就是这个程序的所带的参数
最后是一个NULL
#include <unistd.h>
#include <stdio.h>
int main(void)
{
int ret = -1;
printf(“old begin…\n”);
ret = execl("/bin/ls", “ls”, “-la”, NULL);
if (-1 == ret)
{
perror("");
}
printf(“old end…\n”);
return 0;
}
程序打印:printf(“old begin…\n”);,,,但是并不会打印:printf(“old end…\n”);,说明exec族函数不会返回


execlp函数举例:从PATH环境变量中寻找要去运行的程序
第一个参数:从PATH环境变量中要寻找的程序的名字
第二个参数:name
后面的就是这个程序的所带的参数
最后是一个NULL
#include <unistd.h>
#include <stdio.h>
int main(void)
{
int ret = -1;
printf(“old begin…\n”);
ret = execlp(“ls”, “ls”, “-la”, NULL);
if (-1 == ret)
{
perror("");
}
printf(“old end…\n”);
return 0;
}
和execl函数有些相似,不同的就是第一个参数,execl要求给出全路径,execlp只要给出程序的名字,它会自己去从PATH环境变量中寻找


execle函数举例:将新定义的环境变量传递给需要替换的进程,原来的环境变量将不再起作用,带l所以也要求给出全路径
使用export命令可以看到当前用户所有的环境变量

#include <stdio.h>
#include <unistd.h>

int main()
{
printf(“old begin…\n”);
char * new_env[] = {“CC=arm-linux-gcc”, “ARCH=arm”, NULL}; //定义新的环境变量
execle("./test", “test”, NULL, new_env); //去运行test程序的时候,把新定义的环境变量传过去
printf(“old end…\n”);
return 0;
}
这个程序运行之后会打印:
old begin…
CC=arm-linux-gcc
ARCH=arm

以下对应test程序:
#include <stdio.h>
extern char **environ;

int main()
{
int i = 0;
for (; environ[i] != NULL; i++)
{
printf("%s\n", environ[i]);
}
return 0;
}


execv函数举例:使用全路径pathname,,,用argv数组来传递name和参数
#include <stdio.h>
#include <unistd.h>
int main()
{
printf(“old begin…\n”);
char *argv[] = {“ls”, “-a”, “-l”,"-h", NULL};
execv("/bin/ls", argv);
printf(“old end…\n”);
return 0;
}

execvp和execv的例子程序相似,修改这里就可以了,execvp(“ls”, argv); 不用给出全路径pathname
#include <stdio.h>
#include <unistd.h>

int main()
{
printf(“old begin…\n”);
char *argv[] = {“ls”, “-a”, “-l”,"-h", NULL};
execvp(“ls”, argv);
printf(“old end…\n”);
return 0;
}

execvpe和execvp相似,可以定义新的环境变量传到要执行的程序。感觉这函数没太大的用,应该是execve是有用的
chat * new_env[] = {“CC=arm-linux-gcc”, “ARCH=arm”, NULL};
execvp(“ls”, argv, new_env);

其实以上的6个函数最终调用的都是API: execve

waitpid 函数是使父进程阻塞,直到子进程结束退出或者子进程接到了一个
指定的信号为止。


部分素材来源于网络,如有侵权,请联系作者删除,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值