1.查看进程:
ps axf
ps -ef|book 查看所有为包含book的进程
2.fork后父子进程区别:
拷贝,克隆,一模一样
fork返回值不同 pid不同 ppid不同
未决信号和文件锁不继承,资源利用量清0.
3.fork前需要执行刷新流操作: fflush(null);
进程状态STAT:
R (TASK_RUNNING),可执行状态:
只有在该状态的进程才可能在CPU上运行。而同一时刻可能有多个进程处于可执行状态,这些进程的task_struct结构(进程控制块)被放入对应CPU的可执行队列中(一个进程最多只能出现在一个CPU的可执行队列中。
S (TASK_INTERRUPTIBLE),可中断的睡眠状态:
等待信号量,socket,被放入等待队列中。
D (TASK_UNINTERRUPTIBLE),不可中断的睡眠状态:
不响应信号量,无法被kill-9杀死。存在意义:内核的某些处理流程是不能被打断的。
T (TASK_STOPPED or TASK_TRACED),暂停状态或跟踪状态:
向进程发送一个SIGSTOP信号,它就会因响应该信号而进入TASK_STOPPED状态。
向进程发送一个SIGCONT信号,可以让其从TASK_STOPPED状态恢复到TASK_RUNNING状态。
Z (TASK_DEAD - EXIT_ZOMBIE),退出状态,进程成为僵尸进程。
X (TASK_DEAD - EXIT_DEAD),退出状态,进程即将被销毁。
孤儿进程:
父进程结束,而子进程还在运行,其资源由Init进程(PID=1)接管。
僵尸进程:
子进程结束,但是父进程未使用wait函数进行收尸,导致其变为僵尸进程。
vfork():
子进程和父进程共用同样的数据块,且只能执行exec函数簇 或者 exit退出。
fork():
子进程和父进程共用同样的数据块,但是当存在写操作时,其中一个进程会拷贝另一个进程的数据到一个新的数据块中(谁写谁拷贝)。
进程终止
5种正常终止:
1.main执行 return
2.进程调用exit
3.进程调用_exit或_EXIT
4.最后一个线程从其启动例程返回
5.最后一个线程调用pthread_exit
3种异常终止:
1.调用 abort 产生SIGABRT
2.接到一个信号并终止
3.最后一个线程对取消请求做出响应
进程销毁
wait()函数 子进程改变状态 父进程才可以调用该函数
1.wait(*int status)
阻塞等待
当参数不为空时,会将进程状态信息保存到status指针指向的Int地址中
返回值:
成功:返回终止进程的pid号 失败:-1
2.waitpid(pid_t pid,*int status,int option)
非阻塞等待 使用option
参数1:子进程号
pid<-1 销毁进程组识别码为 pid 绝对值的任何子进程。
pid=-1 销毁任意的子进程 相当于wait()
pid==0 销毁进程组识别码与目前进程相同的任何子进程。
pid>0 销毁任何子进程识别码为 pid 的子进程。
参数2:进程状态
参数3:选项
WNOHANG 如果没有子进程退出 立即返回
linux管道命令: |wc 计算字数:
-c或--bytes或--chars 只显示Bytes数。
- l或--lines 显示行数。
-w或--words 只显示字数。
--help 在线帮助。
--version 显示版本信息。
1.多进程 筛选质数(交叉分配法)
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>
//质数范围
#define Left 30000000
#define Right 30000200
#define N 3//corss assignment 3个进程
//find zhishu
int main()
{
int mark;
for(int n=0;n<N;n++)
{
pid_t pid=fork();
if(pid<0)
{
perror("fork failure");
for(int j=0;j<n;j++) wait(NULL);
exit(1);
}
if(pid==0)
{
for(int i=Left+n;i<=Right;i+=N)
{
mark=1;
for(int j=2;j<i/2;j++)
{
if(i%j==0) {
mark=0;break;}
}
if(mark)
printf("[%d]%d is a primer\n",n,i);
}
exit(0);//子进程退出
}
}
for(int n=0;n<N;n++)
wait(NULL);//回收子进程号
exit(0);
}
exec函数族
由一个新的进程映像替换当前进程
执行一个文件
int execl(const char* path,const char* arg,…);
参数1:可执行二进制文件路径
参数2:命令
err=execl("/bin/date",“date”,"+%s",NULL);
返回值err:-1失败
#include<unistd.h>
#include<stdlib.h>
#include<stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
puts("begin!");
fflush(NULL);
pid_t pid=fork();
if(pid<0){perror("fork");exit(1);};
if(pid==0){
execl("/bin/date","date","+%s",NULL);//进程替换 执行date+%s
perror("execl()"); exit(1);//如果替换不成功执行这句
}
wait(NULL);//父进程销毁子进程
puts("end!");
exit(0);
}
使用exec函数族实现shell外部命令
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<glob.h>
#include <sys/types.h>
#include <sys/wait.h>
#define DELIMS " \t\n" //分隔符
/*typedef struct {
size_t gl_pathc; /* Count of paths matched so far
char **gl_pathv; /* List of matched pathnames.
size_t gl_offs; /* Slots to reserve in gl_pathv.
} glob_t;
*/
struct cmd_st
{
glob_t globres;//结构体可以多个类型
};
static void prompt(void)
{
printf("myshell-0.1$");
}
static void parse(char *line,struct cmd_st *res)
{
char *tok;
int i=0;
while(1){
tok=strsep(&line,DELIMS);
// 将line 中的长串内容 以DELIMS的分隔符的形式 分割为若干子串 返回值为指针
if(tok==NULL) break;
if(tok[0]=='\0') continue;
glob(tok,GLOB_NOCHECK|GLOB_APPEND*i,NULL,&res->globres);
//动态存储分割得到的子串
// GLOB_NOCHECK
//GLOB_APPEND追加(第一次不追加 以后每次都追加i初始为0 因此初始不追加)
//&res->globres存储的地址
i=1;
}
}//拆分命令串
int main()
{
char *linebuf=NULL;
size_t linebuf_size=0;
struct cmd_st cmd;
//glob_t globres;
while(1)
{
prompt();
if(getline(&linebuf,&linebuf_size,stdin)<0) break;
parse(linebuf,&cmd);//CMD存储命令
if(0){/*do sth*/}//如果是内部命令 比如cd exit
else{
//外部命令
pid_t pid=fork();
if(pid<0){perror("fork failure");
exit(1);
}
if(pid==0){
execvp(cmd.globres.gl_pathv[0],cmd.globres.gl_pathv);//子进程被新的进程映像替换
//环境变量下的exec函数
// int execvp(const char *file, char *const argv[]);
perror("execvp faliure");exit(1); }
else wait(NULL);//父进程
}
}
exit(0);
}