linux进程状态,linux进程号函数,linux进程

 2023-09-23 阅读 21 评论 0

摘要:一、进程的含义进程是运行中的程序,进程是操作系统资源分配的基本单位/最小单位。linux进程状态、进程在内存,程序在硬盘(程序代码在硬盘,运行时转存到内存)同时运行2个程序时,它们是运行在物理内存的2个互不相关的地址上例1:查看进程号命

一、进程的含义

进程是运行中的程序,进程是操作系统资源分配的基本单位/最小单位。

linux进程状态、进程在内存,程序在硬盘(程序代码在硬盘,运行时转存到内存)

同时运行2个程序时,它们是运行在物理内存的2个互不相关的地址上

例1:

查看进程号命令、// 001.c

#include

#include

linux进程号PID什么意思。int a;

int main()

{

一个具有唯一进程id号的shell?int b;

printf("a is %p, b is %p\n", &a, &b); // 查看a和b的地址

while(1)

shell进程。{

sleep(1);

}

shell运行?return 0;

}

b9a6a02e37d0

说明:

linux根据进程号查看端口,Q1:2个程序运行时,为何a的地址是一样的?

显示的地址都是虚拟地址,而非物理地址。

CPU中的MMU(内存管理单元)会根据不同的进程(进程号),将虚拟地址映射到不同的物理地址上。

Q2:2个程序运行时,为何a的地址一样,而b的地址是不一样的?

a是全局变量,存放在数据段;b是局部变量,存放在栈段。

编译完后,数据段的全局变量的地址是固定好的;栈是运行时才会分配地址的,是动态的,因此每次运行时地址都是不同的。

二、进程终止方式

1、return

2、exit

// return与exit基本是等价的

return 0;

exit(0);

// 程序结束的时候,既可以用return 0; 也可以用exit(0)。

// 二者唯一的区别:

// return 0; 返回时,会将程序控制权先交给程序(返回给函数)

// exit(0); 退出时,会将程序控制权直接交给内核

3、_exit

exit与_exit的区别:

1、在不同的头文件中定义的。

// man exit

#include

void exit(int status);

// man _exit

#include

void _exit(int status);

2、exit在退出时,会先做一些清理操作(比如:关闭文件描述符),这样会使输出缓存中的数据被刷新一下;_exit不会做。

如:文件IO中,用到一个行缓存,没有用\0,暂时不会被打印出来的。用return或exit,就会将信息刷到屏幕上/文件中。而用_exit是不会做清理的,在退出时若没有打印出来,就一直不会被打印出来。

三、进程创建

1、fork函数

通过fork函数创建进程。

创建进程也可以叫fork一个进程。

Linux系统中的进程,都是由已有的进程派生出来的,即:父进程创建子进程。除了OS启动时,会创建一个init进程外,其他进程都是通过fork创建的。

vfork:最终也是调用了fork。

fork调用成功后,会派生出子进程;若调用失败,会返回-1。

子进程会继承父进程的几乎所有的数据(包括 数据段、代码段、用户id、主id、创建的文件描述符等),相当于父进程的一个拷贝(整个程序都会拷贝),唯一的区别是pid号不一样。

fork之后,就是2个进程在运行,2个进程是相互独立的。

CPU先调用哪个进程,是不能确定的。

例2:

// 002.c

#include

#include

int main()

{

pid_t pid = 0;

printf("=====\n"); // =====存在于数据段

pid = fork();

if(pid < 0)

{

perror("fork");

return -1;

}

printf("ok!\n");

return 0;

}

b9a6a02e37d0

父进程与子进程都打印了ok!

fork之后,会产生2个返回值:若返回值>0,则是父进程;若返回值==0,则是子进程。

例3:

// 003.c

#include

#include

int main()

{

pid_t pid = 0;

printf("=====\n");

pid = fork();

if(pid < 0)

{

perror("fork");

return -1;

}

else if(pid == 0)

{

printf("it is child process\n");

}

else if(pid > 0)

{

printf("it is parent process\n");

}

printf("ok!\n");

return 0;

}

b9a6a02e37d0

2、进程ID

进程创建后,就会得到一个唯一标识这个进程的ID(进程号)。

include

include

pid_t getpid(void); // 得到当前调用函数的进程的id

pid_t getppid(void); // 得到当前调用函数的进程的父进程的id

例4:

// 004.c

#include

#include

int main()

{

pid_t pid;

printf("=====\n");

pid = fork();

if(pid < 0)

{

perror("fork");

return -1;

}

else if(pid == 0)

{

printf("child pid is %d, parent pid is %d\n", getpid(), getppid());

printf("it is child process\n");

}

else if(pid > 0)

{

printf("parent pid is %d, parent parent pid is %d\n", getpid(), getppid());

printf("it is parent process\n");

}

printf("ok!\n");

return 0;

}

b9a6a02e37d0

子进程先退出,退出时父进程的pid可以显示出来

bash fork得到父进程,父进程fork得到子进程。

b9a6a02e37d0

父进程先退出,child的父进程无从得知,child就变成孤儿进程;孤儿进程会被1号init进程收养

四、进程运行时

1、回收子进程

Q:若不想让子进程变为孤儿进程,该如何做?

(1)使用sleep()函数:等待多长时间

例5:

// 005.c

#include

#include

int main()

{

pid_t pid;

printf("=====\n");

pid = fork();

if(pid < 0)

{

perror("fork");

return -1;

}

else if(pid == 0)

{

printf("child pid is %d, parent is %d\n", getpid(), getppid());

printf("it is child process\n");

}

else if(pid > 0)

{

sleep(1); // 睡1s,保证子进程先退出,然后再运行父进程

printf("parent pid is %d, child is %d\n", getpid(), pid);

printf("it is parent process\n");

}

printf("ok!\n");

return 0;

}

b9a6a02e37d0

先运行子进程,再运行父进程,保证子进程先结束.png

(2)使用getchar函数:等待键盘输入

例6:

// 006.c

#include

#include

int main()

{

pid_t pid;

printf("=====\n");

pid = fork();

if(pid < 0)

{

perror("fork");

return -1;

}

else if(pid == 0)

{

printf("child pid is %d, parent is %d\n", getpid(), getppid());

printf("it is child process\n");

}

else if(pid > 0)

{

getchar(); // 等待键盘输入

printf("parent pid is %d, child is %d\n", getpid(), pid);

printf("it is parent process\n");

}

printf("ok!\n");

return 0;

}

b9a6a02e37d0

子进程运行结束了,而父进程一直没有运行。此时,子进程将变为僵尸态

b9a6a02e37d0

ps aux,看到a.out为Z+,即为僵尸态

2、进程状态

b9a6a02e37d0

进程状态

运行态:进程正在运行或在运行队列中等待

不可中断等待态:进程必须等待某个事件完成,在等待中不可以被信号或者定时器唤醒

可中断等待态:进程等待过程中可以被信号或定时器唤醒

停止状态:进程收到信号,如:SIGSTOP、SIGSTP、SIGIN、SIGOUT

僵尸态:子进程先于父进程结束,子进程变为僵尸态(子进程虽然结束了,但是资源仍然占用着),父进程需要对子进程资源进行回收即可解除僵尸态(调用wait函数)

3、wait函数

DESCRIPTION

All of these system calls are used to wait for state changes in a child of the calling process, and obtain information about the child whose state has changed. A state change is considered to be: the child terminated; the child was stopped by a signal; or the child was resumed by a signal. In the case of a terminated child, performing a wait allows the system to release the resources associated with the child; if a wait is not performed, then the terminated child remains in a "zombie" state.

If a child has already changed state, then these calls return immediately. Otherwise they block until either a child changes state or a signal handler interrupts the call (assuming that system calls are not automatically restarted using the SA_RESTART flag of sigaction(2)). In the remainder of this page, a child whose state has changed and which has not yet been waited upon by one of these system calls is termed waitable.

// man wait:

#include

#include

pid_t wait(int *status); // 只能等到第一个子进程退出

// 父进程调用wait后,父进程就会阻塞,直到子进程运行结束,wait继续运行,此时父进程就对子进程的资源进行回收

pid_t waitpid(pid_t pid, int *status, int options); // 可以等待指定的子进程退出

wait() and waitpid()

The wait() system call suspends execution of the calling process until one of its children terminates. The call wait(&status) is equivalent to:

waitpid(-1, &status, 0);

The waitpid() system call suspends execution of the calling process until a child specified by pid argument has changed state. By default, waitpid() waits only for terminated children, but this behavior is modifiable via the options argument.

例7:

// 007.c

#include

#include

int main()

{

pid_t pid;

pid_t pid2;

printf("=====\n");

pid = fork();

if(pid < 0)

{

perror("fork");

return -1;

}

else if(pid == 0)

{

printf("child pid is %d, parent is %d\n", getpid(), getppid());

printf("it is child process\n");

}

else if(pid > 0)

{

getchar();

pid2 = wait(0); // 也可以不用返回值pid2。使用pid2意义不大,因为一旦wait返回,肯定子进程退出了;若wait不返回一直阻塞,说明一直在等待

// wait中是一个指针,此处不关心此指针,写一个0

printf("pid2 is %d\n", pid2);

printf("parent pid is %d, child is %d\n", getpid(), pid);

printf("it is parent process\n");

}

printf("ok!\n");

return 0;

}

b9a6a02e37d0

一直在等待键盘输入,a.out是一个僵尸态

b9a6a02e37d0

ps aux,看到a.out为Z+,即为僵尸态

b9a6a02e37d0

wait返回的值就是子进程的进程id

例8:

// 008.c

#include

#include

#include

#include

int main()

{

pid_t pid;

pid_t pid2;

int status;

printf("=====\n");

pid = fork();

if(pid < 0)

{

perror("fork");

return -1;

}

else if(pid == 0)

{

printf("child pid is %d, parent is %d\n", getpid(), getppid());

printf("it is child process\n");

}

else if(pid > 0)

{

getchar();

pid2 = wait(&status);

printf("pid2 is %d\n", pid2);

printf("exit status is %d\n", WEXITSTATUS(status));

printf("parent pid is %d, child is %d\n", getpid(), pid);

printf("it is parent process\n");

}

printf("ok!\n");

return 0;

}

b9a6a02e37d0

子进程在打印ok!并执行到return 0;时,会把0作为退出码;程序将状态码status通过宏定义的方式解析出来,返回exit status

五、子进程执行新程序

子进程若是拷贝父进程,是没有意义的;子进程必须执行新程序,才有创建的意义。

方法:使用exec函数族。

// man exec:

execl, execlp, execle, execv, execvp, execvpe - execute a file

#include

extern char **environ;

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

// const char *path:只读的路径(相对路径/绝对路径)。

// const char *arg:参数,是字符串。它是可执行文件的名字。

// ...:可变参数

// 若无参数,写 NULL;不管有没有参数,最后结尾的必须是 NULL (为了告诉函数,参数到此为止)

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

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

int execv(const char *path, char *const argv[]);

// 将参数写进一个数组中,因此不需要可变参数。结尾也是NULL。

int execvp(const char *file, char *const argv[]);

int execvpe(const char *file, char *const argv[], char *const envp[]);

例9:

(1)

// address.c

#include

#include

int a;

int main()

{

int b;

printf("a is %p, b is %p\n", &a, &b);

return 0;

}

(2)

// 009.c

#include

#include

#include

#include

int main()

{

pid_t pid;

pid_t pid2;

int status;

printf("=====\n");

pid = fork();

if(pid < 0)

{

perror("fork");

return -1;

}

else if(pid == 0)

{

printf("child pid is %d, parent is %d\n", getpid(), getppid());

printf("it is child process\n");

execl("./address", "address", NULL);

printf("after execl\n");

}

else if(pid > 0)

{

getchar();

printf("parent pid is %d, child is %d\n", getpid(), pid);

printf("it is parent process\n");

}

printf("ok!\n");

return 0;

}

b9a6a02e37d0

结果说明:

1)运行009时,在子进程中,把address程序也运行了;

2)没有打印出after execl字符串,也只打印了一个ok!:

当子进程调用execl后,子进程就将代码完全替换成了新程序:

子进程的代码段替换成新程序的代码段

原有的数据段和堆段还有其他数据都废弃掉,全部更新为新程序的数据段和堆段)

唯一保留的是进程ID

并开始执行新程序。

因此,execl后面的代码都不起作用了,因此没有打印出after execl字符串,也只打印了一个ok!。

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/3/89675.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息