1. 进程相关概念(414.1)
问1. 什么是程序,什么是进程,有什么区别?
- 程序是静态的概念,gcc xxx.c --o pro,磁盘中生成pro文件,叫做程序
- 进程是程序的一次运行活动,通俗点即程序跑起来了,系统中就多了一个进程
问2. 如何查看系统中有哪些进程?
- a. 使用ps指令查看进程,实际工作中配合grep来查找程序中是否存在某一进程
c
ps// 仅显示当前终端会话中运行的进程的快照
ps aux// 仅显示当前终端会话中运行的进程的快照
ps aux|grep xxx//显示包含特定进程名称xxx的进程。
- b. 使用
top
指令查看进程实时情况,类似windows任务管理器
问3. 什么是进程标识符?
- 每个进程都有一个非负整数表示的唯一ID,叫做pid,类似身份证
- Pid=0:称为交换进程(swapper),作用---进程调度
- Pid=1:init进程,作用---系统初始化
- 编程调用 getpid 函数获取自身的进程标识符,getppid 获取父进程的进程标识符
- PRO/demo1.c
c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(){
pid_t pid;
pid = getpid();//获取自身的进程标识符
printf("my pid is %d\n",pid);
while(1);
return 0;
}
问4. 什么叫父进程,什么叫子进程
- 进程A创建了进程B:
- 那么A叫做父进程,B叫做子进程
- 父子进程是相对的概念,理解为人类中的父子关系
问5. C程序的存储空间是如何分配?(*面试会问)


2. 创建进程函数fork的使用(415.2)
使用fork函数创建一个进程:pid_t fork(void);
- fork函数调用成功,返回两次
- 返回值为 0, 代表当前进程是子进程
- 返回值 非负数(>0),代表当前进程为父进程
- 调用失败,返回 -1
- 每一次a.out之后就是当前进程结束之后,重新a.out就是新的进程,so每个pid号不一样
- FILE/demo3.c
- FILE/demo4.c
c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(){
pid_t pid;
pid_t pid2;
pid = getpid();
printf("before fork: pid = %d\n\n",pid );
fork();
pid2 = getpid();
printf("after fork: pid = %d\n",pid2 );
if(pid == pid2){
printf("this is father print,pid = %d\n\n",pid);fork创建之前的父进程id
}
else{
printf("this is child print,child pid = %d\n",getpid());//fork创建之后的子进程id
}
return 0;
}

- FILE/demo5.c
c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(){
pid_t pid;
printf("father: id =%d\n",getpid());
pid = fork();
if(pid > 0){
printf("this is father print,pid = %d\n",getpid());
}
else if(pid == 0){
printf("this is child print,pid = %d\n",getpid());
}
return 0;
}
3. 创建进程函数fork的使用补充(416.3)
- FILE/demo6.c(fork的其一非正返回值表父进程,等于子进程pid号)
4. 进程创建发生了什么事(417.4)
旧版本Linux的拷贝:全拷贝
新版本Linux的拷贝:写实拷贝(Copy-On-Write,COW)(*面试会问)
-
父子进程在初始阶段共享所有的数据(全局、 栈区、 堆区、 代码), 内核会将所有的区域设置为只读。 当父子进程中任意一个进程试图修改其中的数据时, 内核才会将要修改的数据所在的区域(页) 拷贝一份。
-
共享数据
-
写时拷贝后:
-
FILE/demo7.c
c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(){
pid_t pid;
int data = 10;
printf("father: id =%d\n",getpid());
pid = fork();
if(pid > 0){
printf("this is father print,pid = %d\n",getpid());
}
else if(pid == 0){
printf("this is child print,pid = %d\n",getpid());
data = data+100;
}
printf("data=%d\n",data);
return 0;
}

5. 创建新进程的实际应用场景及fork总结(418.5)
fork创建一个子进程的一般目的

fork编程实战
- PRO/demo8.c
c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(){
pid_t pid;
int data = 10;
while(1){
printf("please input a data\n");
scanf("%d",&data);
if(data == 1){
pid = fork();
if(pid > 0){
}
else if(pid == 0){
while(1){
printf("do net request,pid=%d\n",getpid());
sleep(3);//使程序休眠3秒
}
}
}
else{
printf("wait ,do nothing\n");
}
}
return 0;
}
6. vfork创建进程(419.6)

- vfork 与 fork 区别
- vfork 直接使用父进程存储空间,不拷贝。
- vfork 保证子进程先运行,当子进程调用exit退出后,父进程才执行。
- PRO/demo9.c、demo10.c
c
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(){
pid_t pid;
pid = fork();
//pid = vfork();
if(pid > 0){
while(1){
printf("this is father print, pid = %d\n",getpid());
sleep(3);
}
}
else if(pid == 0){
while(1){
printf("this is chilid print, pid = %d\n",getpid());
sleep(3);
}
}
return 0;
}
- PRO/demo11.c、demo12.c
c
#include <stdio.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>//exit函数的
int main(){
pid_t pid;
int cnt = 0;
pid = vfork();
if(pid > 0){
while(1){
printf("cnt=%d\n",cnt);
printf("this is father print, pid = %d\n",getpid());
sleep(1);
}
}
else if(pid == 0){
while(1){
printf("this is chilid print, pid = %d\n",getpid());
sleep(1);
cnt++;
if(cnt == 3){
exit(0);
//_exit(0);
//_Exit(0);
}
}
}
return 0;
}
7. 进程退出(420.7)
-
正常退出
- Main函数调用return
- 进程调用exit(),标准c库
- 进程调用_exit()或者_Exit(),属于系统调用
- 进程最后一个线程返回
- 最后一个线程调用pthread_exit
-
异常退出
- 调用abort
- 当进程收到某些信号时,如ctrl+C
- 最后一个线程对取消(cancellation)请求做出响应
8. 父进程等待子进程退出 (一)(421.8)
为啥要等待子进程退出

父进程等待子进程退出并收集子进程的退出状态
- status参数:是一个整型数指针
- 非空:子进程退出状态放在它所指向的地址中
- 空:不关心退出状态
- PRO/demo14.c
- PRO/demo15.c
子进程退出状态不被收集,变成僵死进程(僵尸进程)
- PRO/demo12.c(同demo11)(vfork,子进程退出后再父进程,子进程Z+僵尸进程)
- PRO/demo13.c(fork无wait收集,子进程为僵尸进程,且父子进程先是交替运行)
9. 父进程等待子进程退出 (二)(422.9)
waitpid
- PRO/demo16.c(waitpid(pid,&status,WNOHANG);//不常用,不挂起不等待退出)
孤儿进程
- 父进程如果不等待子进程退出,在子进程之前就结束了自己的"生命",此时子进程叫做孤儿进程
- Linux避免系统存在过多孤儿进程,init进程收留孤儿进程,变成孤儿进程的父进程(pid=1)
- PRO/demo17.c
unix环境高级编程
- 代码(子进程退出的状态判断)
运行结果:
10. exec族函数(423.10)
为什么要用exec族函数,有什么作用

- PRO/echoarg.c()
c
#include <stdio.h>
int main(int argc,char *argv[]){
int i = 0;
for(i = 0; i < argc; i++){
printf("argv[%d]: %s\n",i,argv[i]);
}
return 0;
}

- PRO/demo18.c
c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);
int main(void){
printf("before execl\n");
if(execl("./echoarg","echoarg","abc",NULL) == -1){
printf("execl failed!\n");
perror("why");
}
printf("after execl\n");
return 0;
}

- PRO/demo19.c
c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);
int main(void){
printf("before execl\n");
if(execl("/bin/ls","ls",NULL,NULL) == -1){
printf("execl failed!\n");
perror("why");
}
printf("after execl\n");
return 0;
}

- PRO/demo20.c
c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);
int main(void){
printf("before execl\n");
if(execl("/bin/ls","ls","-l",NULL) == -1){
printf("execl failed!\n");
perror("why");
}
printf("after execl\n");
return 0;
}

- PRO/demo21.c
c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);
int main(void){
printf("this pro get system date:\n");
if(execl("/bin/date","date",NULL,NULL) == -1){
printf("execl failed!\n");
perror("why");
}
printf("after execl\n");
return 0;
}

- PRO/demo22.c(execlp函数带p,能通过环境变量PATH查找到可执行文件ps)(带p的只是省略了绝对路径,编译结果都一样)
c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);
int main(void){
printf("this pro get system date:\n");
if(execlp("ps","ps",NULL,NULL) == -1){
printf("execl failed!\n");
perror("why");
}
printf("after execl\n");
return 0;
}
echo $PATH
:让系统能找到这个路径底下的可执行程序
- 将当前路径添加到系统环境变量(不用"./"、在别的目录下也可直接执行那个程序)
c
pwd//return:/home/cjq/Jessie/PRO
export PATH=$PATH:/home/cjq/Jessie/PRO
- PRO/demo23.c(编译结果同demo22)(带v的只是多了一个char *argv)
c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);
int main(void){
printf("this pro get system date:\n");
char *argv[] = {"ps",NULL,NULL};
if(execvp("ps",argv) == -1){
printf("execl failed!\n");
perror("why");
}
printf("after execl\n");
return 0;
}
- PRO/demo24.c(编译结果同demo22、23)(带v的只是多了一个char *argv)(不带p要加上绝对路径)
c
char *argv[] = {"ps",NULL,NULL};
if(execv("/bin/ps",argv) == -1){
11. exec族函数配合fork使用(11)

- 实现功能:当父进程检测到输入为1的时候,创建子进程把配置文件的字段值修改掉
- PRO/demo25.c(仅使用子进程方式修改配置文件中的参数)
c
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>//wait的
#include <sys/wait.h>//wait的
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(){
pid_t pid;
int data = 10;
while(1){
printf("please input a data\n");
scanf("%d",&data);
if(data == 1){
int fdSrc;
pid = fork();
if(pid > 0){
wait(NULL);
}
if(pid == 0){
char *readBuf=NULL;
fdSrc = open("config.txt",O_RDWR);
int size = lseek(fdSrc,0,SEEK_END);
lseek(fdSrc,0,SEEK_SET);
readBuf=(char *)malloc(sizeof(char)*size + 8);
int n_read = read(fdSrc, readBuf, size);
char *p = strstr(readBuf,"LENG=");
if(p==NULL){
printf("not found\n");
exit(-1);
}
p = p+strlen("LENG=");
*p = '5';
lseek(fdSrc,0,SEEK_SET);
int n_write = write(fdSrc,readBuf,strlen(readBuf));
close(fdSrc);
exit(0);
}
}
else{
printf("wait ,do nothing\n");
}
}
return 0;
}
- PRO/demo26.c(子进程用exec调用另一程序来修改配置文件中的参数)
c
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>//wait的
#include <sys/wait.h>//wait的
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(){
pid_t pid;
int data = 10;
while(1){
printf("please input a data\n");
scanf("%d",&data);
if(data == 1){
int fdSrc;
pid = fork();
if(pid > 0){
wait(NULL);
}
if(pid == 0){
execl("./changeData","changeData","config.txt",NULL);
}
}
else{
printf("wait ,do nothing\n");
}
}
return 0;
}
12. system函数(424.12)

- 精彩博文:linux system 函数详解
- system()函数的返回值如下:
- 成功,则返回进程的状态值;
- 当sh不能执行时,返回127;
- 失败返回-1;
- PRO/demo27.c
c
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>//wait的
#include <sys/wait.h>//wait的
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(){
pid_t pid;
int data = 10;
while(1){
printf("please input a data\n");
scanf("%d",&data);
if(data == 1){
int fdSrc;
pid = fork();
if(pid > 0){
wait(NULL);
}
if(pid == 0){
//execl("./changeData","changeData","config.txt",NULL);
system("./changeData config.txt");//相当于在终端执行shell命令"sh -c ./changeData config.txt"
}
}
else{
printf("wait ,do nothing\n");
}
}
return 0;
}

13. popen函数(425.13)

- 精彩博文:popen
- 比system在应用中的好处:
- 可以获取运行的输出结果
- PRO/demo28.c(system(exec)运行成功不返回)
c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);
int main(void){
char ret[1024] = {0};
system("ps");
printf("ret=%s\n",ret);
//popen("ps","w");
return 0;
}
- PRO/demo29.c(popen返回运行结果)
c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//函数原型:int execl(const char *path, const char *arg, ...);
//size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
int main(void){
char ret[1024] = {0};
FILE *fp;
fp = popen("ps","r");
int nread = fread(ret,1,1024,fp);
printf("read ret %d byte, ret=%s\n",nread,ret);
return 0;
}