网站站点建设,专业做网站建设公司哪家好,房屋设计软件app哪个好,网站内容的设计与实现目录 一、概念1.1 程序1.2 进程 二、特点⭐⭐⭐三、进程段四、进程分类五、进程状态六、进程状态转换图七、函数接口1. 创建子进程2. 回收进程资源3. 退出进程4. 获取进程号 八、守护进程 一、概念 进程和程序是密不可分的两组概念#xff0c;相对比#xff0c;便于理解。 1.… 目录 一、概念1.1 程序1.2 进程 二、特点⭐⭐⭐三、进程段四、进程分类五、进程状态六、进程状态转换图七、函数接口1. 创建子进程2. 回收进程资源3. 退出进程4. 获取进程号 八、守护进程 一、概念 进程和程序是密不可分的两组概念相对比便于理解。 1.1 程序
简单来说程序即编译好的可执行文件
展开来说
编译好的可执行文件存放在磁盘上的指令和数据的有序集合文件静态的没有执行的概念
1.2 进程
程序一次执行过程是动态的包括创建、调度、执行以及消亡它是一个独立的可调度的任务 展开来说
进程是程序的一次执行过程。进程是动态的包含创建、调度、执行、消亡。进程是执行一个程序分配资源的总称。独立的可调度的任务
二、特点⭐⭐⭐
系统会为每一个进程分配0-4g的虚拟空间其中0-3g(用户空间)是每个进程所独有的3g-4g(内核空间)是所有进程共有的。CPU调度进程时会给进程分配时间片几毫秒 ~ 十几毫秒当时间片用完后cpu再进行其他进程的调度实现进程的轮转从而实现多任务的操作。
三、进程段
Linux中的进程包含三个段
“数据段”存放的是全局变量、常数以及动态数据分配的数据空间(如malloc函数取得的空间)等。“正文段”存放的是程序中的代码“堆栈段”存放的是函数的返回地址、函数的参数以及程序中的局部变量
四、进程分类
交互进程该类进程是由shell控制和运行的。交互进程既可以在前台运行也可以在后台运行。该类进程经常与用户进行交互需要等待用户的输入当接收到用户的输入后该类进程会立刻响应典型的交互式进程有shell命令进程、文本编辑器等批处理进程该类进程不属于某个终端它被提交到一个队列中以便顺序执行。守护进程该类进程在后台运行。它一般在Linux启动时开始执行系统关闭时才结束。
五、进程状态
运行态TASK_RUNNINGR指正在被CPU运行或者就绪的状态。这样的进程被成为runnning进程。睡眠态(等待态)
可中断睡眠态TASK_INTERRUPTIBLES处于等待状态中的进程一旦被该进程等待的资源被释放那么该进程就会进入运行状态。不可中断睡眠态TASK_UNINTERRUPTIBLED该状态的进程只能用wake_up()函数唤醒。
暂停态TASK_STOPPEDT当进程收到信号SIGSTOP、SIGTSTP、SIGTTIN或SIGTTOU时就会进入暂停状态。可向其发送SIGCONT信号让进程转换到可运行状态。死亡态X进程结束僵尸态TASK_ZOMBIEZ当进程已经终止运行但还占用系统资源要避免僵尸态的产生例如在父子进程中子进程比父进程先结束而父进程没有对子进程及时回收释放子进程占用的资源此时子进程将变成一个僵尸进程。
六、进程状态转换图
进程创建后进程进入就绪态当CPU调度到此进程时进入运行态当时间片用完时此进程会进入就绪态如果此进程正在执行一些IO操作(阻塞操作)会进入阻塞态完成IO操作阻塞结束后又可进入就绪态等待CPU的调度当进程运行结束即进入结束态。
七、函数接口
1. 创建子进程
pid_t fork(void); 功能创建子进程 参数无 返回值 成功在父进程中返回子进程的进程号 0在子进程中返回值为0失败-1并设置errno 特点⭐⭐⭐ 子进程几乎拷贝了父进程的全部内容包括代码、数据、系统数据段中的pc值、栈中的数据、父进程中打开的文件等但它们的PID、PPID是不同的。父子进程有独立的地址空间互不影响当在相应的进程中改变全局变量、静态变量都互不影响。若父进程先结束子进程成为孤儿进程被init进程收养子进程变成后台进程。若子进程先结束父进程如果没有及时回收子进程变成僵尸进程要避免僵尸进程产生 拓展 fork之前的代码被复制但是不会重新执行一遍fork之后的代码被复制并且再被执行一遍。fork之后两个进程相互独立子进程拷贝了父进程的所有代码但内存空间独立fork之前打开文件fork之后拿到的是同一个文件描述符操作的是同一个文件指针。而不同的进程打开相同的文件操作的是不同的文件指针fork函数创建父子进程后各自执行顺序不一定vfork先执行完子进程再执行父进程 例子
#include stdio.h
#include unistd.h
// 若父进程先结束子进程将变成孤儿进程 且被init进程收养变成孤儿进程
// 若子进程先结束父进程如果没与及时回收子进程将变成僵尸进程
int main(int argc, char const *argv[])
{int a 0;pid_t pid fork();if(pid0){perror(fork err);return -1;}else if(pid 0){ // 运行子进程printf(child a:%d\n,a);while(1);}else{ // 运行父进程a3;printf(parent a:%d\n,a);//while(1);}return 0;
}2. 回收进程资源
waitpid_t wait(int *status); 功能回收子进程资源(阻塞)参数 status子进程退出状态不接受子进程状态设为NULL 返回值 成功回收的子进程的进程号失败-1 waitpidpid_t waitpid(pid_t pid, int *status, int options); 功能回收子进程资源参数 pid status子进程退出状态options 0阻塞WNOHANG非阻塞 返回值 正常结束的子进程的进程号当使用选项WNOHANG且没有子进程结束时0出错-1 例
#include unistd.h
#include stdio.h
#include sys/types.h
#include sys/wait.h
int main(int argc, char const *argv[])
{int a 0;pid_t pid fork();if(pid0){perror(fork err);return -1;}else if(pid 0){printf(in child a%d\n,a);// while(1);sleep(1);}else{a 3;// wait(NULL); //阻塞回收所有子进程// waitpid(-1,NULL,0); //阻塞回收所有子进程waitpid(pid,NULL,0); // 阻塞回收pid进程// waitpid(pid,NULL,WNOHANG); //不阻塞回收进程号为pid的进程printf(in parent a %d\n,a);// while(1);wait(NULL);printf(parent end...\n);}return 0;
}3. 退出进程
exit void exit(int status); 功能结束进程刷新缓存参数退出的状态 _exitvoid _exit(int status); 功能结束进程不刷新缓存参数status是一个整型的参数可以利用这个参数传递进程结束时的状态。 通常0表示正常结束其他的数值表示出现了错误进程非正常结束
注exit与return的区别⭐⭐
exit函数不管在子函数还是主函数都可以结束进程进程的退出return关键字当子函数中有return时返回到函数调用位置并不结束进程函数的退出 例子
#include unistd.h
#include stdio.h
#include sys/types.h
#include sys/wait.h
#include stdlib.hvoid fun(int a, int b)
{printf(ab%d,ab);// exit(0); // 结束进程刷新缓存_exit(0); // 结束进程不刷新缓存return;
}int main(int argc, char const *argv[])
{pid_t pid fork();if(pid0){perror(fork err);return -1;}else if(pid 0){printf(in child\n);fun(3,4);sleep(1);}else{printf(in parent\n);// while(1);wait(NULL);printf(parent end...\n);}return 0;
}运行结果 exit会刷新缓存 _exit不会刷新缓存
4. 获取进程号
getpidpid_t getpid(void); 功能获取当前进程的进程号 getppidpid_t getppid(void); 功能获取当前进程的父进程号 例
#include unistd.h
#include stdio.h
#include sys/types.h
#include sys/wait.h
#include unistd.h
#include stdlib.h
int main(int argc, char const *argv[])
{pid_t pid fork();if(pid0){perror(fork err);return -1;}else if(pid 0){printf(childpid:%d ppid:%d pid:%d\n,getpid(),getppid(),pid);exit(0);}else{printf(parentpid:%d ppid:%d pid:%d\n,getpid(),getppid(),pid);wait(NULL);}return 0;
}运行
练习通过父子进程完成对文件的拷贝(cp)父进程从文件开始到文件的一半开始拷贝子进程从文件的一半到文件末尾。要求文件IO cp src dest
方法一 这里先执行子进程来进行源文件后半部分的复制然后执行父进程来进行源文件前半部分的复制 方法二 这里先执行父进程来进行源文件前半部分的复制后执行子进程来进行源文件后半部分的复制
//通过父子进程完成对文件的拷贝(cp)父进程从文件开始到文件
//的一半开始拷贝子进程从文件的一半到文件末尾。要求文件IO cp src dest
#include unistd.h
#include stdio.h
#include sys/types.h
#include sys/wait.h
#include unistd.h
#include stdlib.h
#include sys/stat.h
#include fcntl.h
int main(int argc, char const *argv[])
{if (argc ! 3){printf(Please input:%s srcfile destfile\n, argv[0]);return -1;}int src open(argv[1], O_RDONLY);if (src 0){perror(srcfile open err);return -1;}int dest open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0666);if (dest 0){perror(destfile open err);return -1;}off_t half lseek(src, 0, SEEK_END) / 2; // 文件长度的一半int flag 0; // 控制下面父子进程执行顺序让父进程先执行ssize_t s;char buf[32] {0};pid_t pid fork();if (pid 0){perror(fork err);return -1;}else if (pid 0) // 子进程 复制源文件后半部分{while (flag 1) // 父进程已复制完毕子进程可以执行{lseek(src, half, SEEK_SET);lseek(dest, half, SEEK_SET);while ((s read(src, buf, 32)) ! 0)write(dest, buf, s);}}else // 父进程 复制源文件前半部分先执行{lseek(src, 0, SEEK_SET);lseek(dest, 0, SEEK_SET);int n half;if (n 32){read(src, buf, n);write(dest, buf, n);flag 1;wait(NULL);}else{while ((s read(src, buf, 32)) ! 0){write(dest, buf, s);n n - s;if (n 32){read(src, buf, n);write(dest, buf, n);flag 1;wait(NULL);}}}}close(src);close(dest);return 0;
}//通过父子进程完成对文件的拷贝(cp)父进程从文件开始到文件
//的一半开始拷贝子进程从文件的一半到文件末尾。要求文件IO cp src dest
#include unistd.h
#include stdio.h
#include sys/types.h
#include sys/wait.h
#include unistd.h
#include stdlib.h
#include sys/stat.h
#include fcntl.h
int main(int argc, char const *argv[])
{if (argc ! 3){printf(Please input:%s srcfile destfile\n, argv[0]);return -1;}int src open(argv[1], O_RDONLY);if (src 0){perror(srcfile open err);return -1;}int dest open(argv[2], O_WRONLY | O_TRUNC | O_CREAT, 0666);if (dest 0){perror(destfile open err);return -1;}off_t half lseek(src, 0, SEEK_END) / 2; // 文件长度的一半int flag 0; // 控制下面父子进程执行顺序让父进程先执行ssize_t s;char buf[32] {0};pid_t pid fork();if (pid 0){perror(fork err);return -1;}else if (pid 0) // 子进程 复制源文件后半部分{while (flag 1) // 父进程已复制完毕子进程可以执行{lseek(src, half, SEEK_SET);lseek(dest, half, SEEK_SET);while ((s read(src, buf, 32)) ! 0)write(dest, buf, s);}}else // 父进程 复制源文件前半部分先执行{lseek(src, 0, SEEK_SET);lseek(dest, 0, SEEK_SET);int n half;if (n 32){read(src, buf, n);write(dest, buf, n);flag 1;wait(NULL);}else{while ((s read(src, buf, 32)) ! 0){write(dest, buf, s);n n - s;if (n 32){read(src, buf, n);write(dest, buf, n);flag 1;wait(NULL);}}}}close(src);close(dest);return 0;
}八、守护进程
特点 守护进程是后台进程生命周期比较长从系统启动时开启系统关闭时结束它是脱离控制终端且周期执行的进程。实现步骤 创建子进程父进程退出 让子进程成为孤儿进程成为后台进程。fork在子进程中创建新会话 让子进程成为会话组组长为了让子进程脱离控制终端。setsid()改变运行路径为根目录。chdir 原因提高权限让运行路径不能被删除或卸载。chdir重设权限掩码。umask 目的增大进程创建文件的权限提高灵活性。umask关闭不必要的文件描述符 目的关闭不必要的文件描述符。close
练习创建一个守护进程循环间隔1s向文件中写入一串字符“hello”
/*创建守护进程实现往日志文件中循环写入hello,间隔时间为1秒*/
#include unistd.h
#include sys/types.h
#include sys/stat.h
#include stdlib.h
#include stdio.h
#include fcntl.hint main(int argc, char const *argv[])
{//1.创建父子进程pid_t pid fork();if(pid 0){perror(fork err);return -1;}else if(pid 0) //子进程进入{setsid(); //2.在子进程中创建新会话让子进程成为会话组组长chdir(/); //3.改变运行目录为根目录umask(0); //4.重设文件掩码close(0);//5.关闭文件描述符-0 1 2close(1);int fd open(/tmp/c.log,O_WRONLY|O_CREAT|O_APPEND,0666);if(fd 0){perror(open err);return -1;}}else{exit(0); //1.父进程退出}return 0;
}