程序的执行方式1 顺序执行 ---- 程序从上到下依次执行我们的程序会死在某一个while(1)里面这个时候假设我还想做其它的事情,这些事情就没有办法完成了2 我在执行某一个while(1)的时候同时希望能运行另外一个while(1)这两个while(1)就是同时执行的 ----- 并行并行 --- 两个东西同时发生,cpu核心里面同一个时间(这个时间很短)只能执行一条指令因此并行在电脑里面就无法进行了虽说我们没有办法实现并行,但是利用cpu的速度我可以让你看起来像并行 --- 并发并发的实现有两种方式1 进程2 线程进程:有独立功能的一个程序在某一些指令集合上面的一次执行通俗来讲就是你写一个代码,里面有一个main,你通过gcc讲这个代码编译成一个a.out./a.out的时候,我们的系统里面就会看到有一个正在执行的a.out这个执行的a.out就是一个进程程序就是一个死的文件,放在哪里,里面有一些指令,一个程序可能对应多个进程进程是一个活的程序,它在执行指令,进程与进程之间是相互独立的进程是竞争系统资源的最小单位,也就是说系统是给进程分配资源的进程如何来实现并发?为了实现并发,进程有三个状态1 就绪态 --- 程序准备好了,可以执行了,但是还没有执行2 运行态 --- 程序的指令放在cpu里面去执行3 休眠态 --- 当运行一段时间之后,发现这个程序没有办法执行完,从而转到这个运行状态这个cpu就会将它t出来当发现睡过头了,不行了,它就会被系统给唤醒,转到就绪态一直切换这三态,程序就可以实现并发了linux进程的api
NAMEfork - create a child process创建一个子进程,子进程一旦创建出来,和父进程就没有关系了
SYNOPSIS#include <sys/types.h>#include <unistd.h>pid_t fork(void);子进程的指令完全拷贝父进程的指令,也就是子进程的代码区完全拷贝的是父进程的内存去也是拷贝的父进程的,这个时候父进程里面的变量的值跟子进程里面的一模一样但是后面发生什么,怎么变的,这两个进程就不同了变量,文件描述符,io缓冲区.....都是在这个拷贝范围之内返回值:父进程里面返回的是子进程的id > 0子进程里面返回0失败返回-1,同时errno被设置进程的运行优先级是看系统调度的练习:1 将fork练习一遍2 有如下代码,最终有几个进程在执行int main(){for(int i = 0;i < 3;i++){fork();}while(1);}最后会有8个进程执行获取进程idNAMEgetpid, getppid - get process identificationSYNOPSIS#include <sys/types.h>#include <unistd.h>pid_t getpid(void);//返回自己的idpid_t getppid(void);//返回自己的爸爸的id返回值:成功返回id号失败返回-1,同时errno被设置NAMEexit - cause normal process termination正常退出进程
SYNOPSIS#include <stdlib.h>void exit(int status);//正常退出 退出的时候会做清理,如果刷新缓冲区NAME_exit, _Exit - terminate the calling process正常退出进程
SYNOPSIS#include <unistd.h>void _exit(int status);//正常退出 退出的时候不会做清理status:退出的时候给一个退出码这两个函数只要被调用到,不管在程序的哪个位置(不在main也是一样的),就会直接将进程给终结发信号杀死进程kill -9 进程号killall -9 进程的名字(相同名字的进程会全部被杀死)NAMEsystem - execute a shell commandSYNOPSIS#include <stdlib.h>int system(const char *command);//这个函数调用到会执行这个shell指令command:你要执行的shell指令 就是一个字符串如:"ls"指令执行完毕就会返回,没有执行完毕会卡在这个执行指令这里system("madplay -Q 1.mp3"); -> 播放的时候是不会返回的,只有播放完毕 system才会返回-> system("madplay -Q 1.mp3 &"); -> 指令转入后台,system会马上返回子进程死亡了,资源是不会被回收的子进程死亡后如果不回收资源,这个进程就会变成僵尸进程(没有谁能杀死一个已经死亡了的进程)这些资源就会被浪费掉僵尸进程很危险,要及时清理掉如果父进程先于子进程退出了,子进程就会变成孤儿进程,孤儿进程会被init进程托管孤儿进程由于没有父亲,因此它可以脱离很多的掌控,如果可以完全从终端独立出来,这种进程我们称为守护进程按道理,一个子进程死了,应该是家里人给它收尸,父进程就应该等待子进程死亡并且回收它的资源NAMEwait, waitpid, waitid - wait for process to change state等待子进程状态发生改变
SYNOPSIS#include <sys/types.h>#include <sys/wait.h>pid_t wait(int *wstatus);//等待任意一个子进程死亡 如果有多个子进程你需要循环等待//阻塞等待wstatus:保存子进程的退出信息我们需要一些宏来解析WIFEXITED(wstatus) ->如果进程是正常退出的,则这个宏返回trueWEXITSTATUS(wstatus) -> 返回进程的退出码,只有WIFEXITED(wstatus)为真,这个宏才有意义WIFSIGNALED(wstatus) -> 如果进程是被信号杀死的,如kill,那么这个宏会返回truepid_t waitpid(pid_t pid, int *wstatus, int options);pid:你要等待哪些子进程pid == -1:表示等待任意一个子进程, == waitpid == 0 :等待调用进程同组的所有的子进程中的一个进程也是有组的,每个组都会有一个组长这个组长的id就称为组idpid < 0 :等待pid绝对值组(绝对值就是组id)的所有的子进程中的一个pid > 0 : 等待id号为pid的那个子进程死亡wstatus:跟上面的这个函数一样 options:等待标志0表示阻塞等待WNOHANG非阻塞等待,如果没有子进程死亡,则这个函数会返回0WUNTRACED等待被信号杀死的.....返回值:成功返回子进程的id,失败返回-1,同时errno被设置练习 wait system getpid getppid我们弄到现在发现子进程和父进程的代码完全一样虽说可以通过fork的返回值来判断子进程还是父进程,但是你无法规避两个进程的代码是完全一样的这么一来弄一个子进程出来就有点扯淡了,这个时候我们就希望这个子进程能去跑一些不一样的代码NAMEexecl, execlp, execle, execv, execvp, execvpe - execute a file运行一个文件,这个文件必须是可执行文件如:我现在有一个main是一个可执行文件,这个main里面有fork子进程我是希望它跑另外一份代码的:如a.out也就是用a.out的代码将main fork出来的子进程的代码给替换掉那么子进程就会去执行a.out的代码了a.out只会替换掉子进程的代码空间,其它的资源还会保留的如:文件描述符,int fd = open();替换代码之后fd这个变量就不在了但是前面open打开的这个文件还在打开着,我们一样的可以去用这个文件替换完毕之后代码从头开始运行
SYNOPSIS#include <unistd.h>extern char **environ;int execl(const char *path, const char *arg, .../* (char *) NULL */);path:路径 你要替换的代码的那个文件的路径名,如果你是c写的,那么这个a.out就是gcc编译完成之后生成的那个文件"./a.out" "/mnt/hgfs/share/main"arg:你运行a.out的时候,main函数里面可能用了一些参数这个参数就是为了想main函数里面传参的main函数里面 用了argv[1] 这里面就要传一个如果有两个继续传 写好多都行,但是一定以NULL结尾记住第一个参数为程序的名字execl("./a.out","a.out","123","abc","456def",NULL);返回值:失败返回-1,同时errno被设置成功没有返回了,成功后就会去运行其它的代码,你让我怎么返回int execlp(const char *file, const char *arg, .../* (char *) NULL */);////在环境变量里面的程序名字int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);//路径名int execv(const char *path, char *const argv[]);//路径名int execvp(const char *file, char *const argv[]);//在环境变量里面的程序名字int execvpe(const char *file, char *const argv[],//在环境变量里面的程序名字char *const envp[]);V:版本是将我们的参数变成一个向量(就是一个数组)通过这个argv向量将所有的参数带入execl("./a.out","a.out","123","abc","456def",NULL);换成v版本就要如下使用char * argv[] = {"a.out","123","abc","456def",NULL};execv("./a.out",argv);p:版本为PATH,你要运行的那个程序在环境变量里面,那么我们就可以不用给路径了,直接给在环境变量里面的那个程序的名字就可以了如:madplay.......execlp("madplay", "madplay","123","abc","456def",NULL);e:这个版本是通过第三个参数传进去一个新的路径,这个路径为这个进程替换代码之后新的环境变量与原程序的环境变量是分开的execle("./a.out","a.out","123","abc","456def",NULL,"/your/new/path");/your/new/path:你新给的环境变量练习:开一个子进程,子进程运行madplay,播放一首歌