进程和信号
由于最近比较忙,已经好久没更新了,完成实验任务的同时就更新下吧;
另外,这时第十一章的内容,直接从shell程序设计跳到了第11章,时间匆忙,暂时这样吧…
进程的结构
如果有两个用户neil和rick,他们同时运行grep程序在不同的文件中查找不同的字符串。他们使用的进程如图11-1所示。
每个进程都会被分配一个唯一的数字编号,称之为进程标识符或PID;当进程被启动时,系统按顺序选择下一个未被使用的数字作为它的PID,当数字已经达到PID可取的最大值,就会回到2开始取未被使用的PID;
一般的为1的PID是特殊进程init保留的,init进程负责管理其他进程;
上图11-1中,grep程序代码保存在磁盘文件中;正常情况下,Linux进程不能对用来存放程序代码的内存进行写操作,即程序代码是以只读方式加载到内存中的,它可以被多个进程安全地共享;
查看进程
ps命令可以显示正在运行的进程、其他用户正在运行的进程或者目前在系统上运行的所有进程;
ps -ef
ps -Af
这里的-f选项显示进程完整信息,-e等价于-A选项显示所有进程
用BSD风格显示
ps ax
下面给出对STAT的描述
进程调度
操作系统根据进程的nice值决定优先级,nice值默认为0,值越大,优先级越低; 通过nice设置进程的nice值,renice命令跳转nice值,下面介绍一些命令
ps -l //可以查看nice值
nice oclock & //创建一个oclock的进程
renice 10 13652 //跳转PID为13652的基础的nice值为10
PPID指父进程的PID。
启动新进程
- 可以在程序内部启动另一个程序,从而创建一个新进程,可以通过库函数system完成,它的声明如下:
#include<stdlib.h>
int system(const char *string);
system函数作用:运行字符串形式的命令并等待该命令执行完成,执行情况如同hell中执行如下命令:
sh -c string
编写程序调用ps命令
1.编写如下代码,保存为system1.c
#include<stdlib.h>
#include<stdio.h>
int main(){
printf("Running ps with system\n");
system("ps ax");
printf("Done.\n");
exit(0);
}
2.编译并运行
执行如下命令
gcc -o system1 system.c
sudo ./system1
可以发现执行system1后自动执行sh -c ps ax,然后才执行ps ax
注意
一般来说,system函数需要用一个shell来启动需要的程序;由于在执行所需程序前启动一个shell,而且对shell的按照情况及使用的环境的依赖大,因此system函数效率不高。
2021-05-26 更新,暂时就到这了
- 替换进程映像
exec系列函数由一组相关的函数组成,他们在进程的启动方式和程序参数的表达方式上各有不同。
exec函数可以把当前进程替换为一个新进程,新进程由path或file参数指定。可以使用exec函数将程序的执行从一个程序切换到另一个程序,原来的程序就不再运行了。
下面给出exec系列函数定义原型:
#include<unistd.h>
char **environ;
int execl(const char *path, const char *arg0, ..., (char *)0);
int execlp(const char *file, const char *arg0, ..., (char *)0);
int execle(const char *path, const char *arg0, ..., (char *)0, char *const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const engv[]);
事实上,这些函数都是通过execve函数实现的;
以p结尾的函数通过搜索PATH环境变量查找可执行文件路径;如果可执行文件不在PATH中,需要把包括目录的绝对路径文件名作为参数传递给函数;
以e结尾的函数,可以通过参数envp传递字符串数组作为新程序的环境变量;
下面给出一个例子
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
int main(){
printf("Running ps with execlp\n");
execlp("ps", "ps", "ax", 0);
printf("Done.\n");
return 0;
}
保存为pexec.c,编译并执行./pexec
复制进程映像
可以调用fork创建一个新进程,这个系统调用复制当前进程,在进程表中创建一个新的表项,它的许多属性与当前进程是相同的(包括代码),但新进程有自己的数据空间、环境和文件描述符;
下面给出fork函数定义:
#include<sys/types.h>
#include<unistd.h>
pid_t fork(void);
父进程调用fork,返回子进程的PID,新进程继续执行,就如原进程一样;但是,子进程调用fork返回0,可以通过它判断是否子进程。
如果fork失败,将返回-1,失败通常由于子进程数太大,超过了上限。
fork与exec函数结合在一起使用就是创建新进程所需的一切了。
等待一个进程
有时,父进程可能开启子进程后就结束了,而子进程还在运行,导致结果输出可能比较乱,这时要用到wait函数,下面给出wait函数定义:
#include<sys/types.h>
#include<sys/wait.h>
pid_t wait(int *stat_loc);
wait系统调用将暂停父进程直到它的子进程结束为止,它返回子进程的PID,通常是已经结束运行的子进程的PID。
2021-05-28 更新