Linux-C-day1-进程基础

进程概念

进程是程序在计算机上执行一次的过程,也就是一个执行中的程序,进程是一个独立的逻辑控制流,独占处理器,相当于工人和机器,进程具有私有的地址空间,独占存储器系统,相当于工厂;进程的本质是程序在地址空间中按照代码逻辑控制流执行的过程,同时进程是资源分配的最小单位;

进程和程序的区别

进程是动态的,并且具有生命周期,并且一个进程只能对应于一个程序;相反,程序是动态的指令集合,一个程序可以对应于多个进程;

进程的状态

就绪:Ready,当进程获得出CPU以外的所有的必要的资源时,进程进入就绪状态,也就是说当进程得到CPU就可以立即运行;
 运行:Runing,进程正在占据CPU资源;
 阻塞(Blocked):进程犹豫等待某个事件发生而无法执行时,进程被迫放弃CPU执行资源;

进程的创建

进程的查看

Windows可以使用tasklist 例如:tasklist / F1 "PID eq 进程PID";
在Linux底下可以使用ps命令进行查看,ps命令的相关选项:
 user:表示进程所属的用户;
 PID:表示进程ID;
 ppid:表示父进程ID;
 %CPU:表示CPU占有率;
 %mem:表示内存占有率;
 VSZ:表示进程虚拟大小;
 RSS:表示常驻内存,也就是内存中页面的数量;
 tty:表示进程使用的终端;
 stat:表示进程的状态;
 start:表示启动进程的时间;
 TIME :表示进程消耗CPU的时间;
 command:表示打开进程的命令和参数;
 进程状态标识符:
  D:表示不可中断进程,Uninterruptible;
  R:表示正在运行,或者在队列中的进程;
  S:处于休眠状态;
  T:进程处于停止或者被追踪状态;
  Z:僵尸进程;
  W:表示进入内存交换,从内核2.6以后开始失效;
  X:表示死掉的进程;
  <:表示高优先级;
  n:表示低优先级;
  s:表示包含子进程;
  +:表示位于后台的进程组;
 查看某个具体的进程:ps -p pid;ps -C在命令行进行查看;同样的还可以使用pstree进行查看,但是可能需要安装yum install psmisc -y;同样的的还可以使用top命令动态的查看系统进程;

进程的杀死

windows:task kill /F /PID 或者task kill /F /IM 程序名
 linux:kill 进程标识PID
 与进程相关的文件一般在/proc/底下,通常是进程id作为目录名的文件;

进程的操作

进程的标识通常使用pid来完成,获取当前进程的id使用函数pid_t getpid();获取当前进程父进程的ID使用函数pid_t getppid();

#include <stdio.h>
#include <unistd.h>

int main(){
    printf("PID:%dPPID:%d\n",getpid(),getppid());//分别用于获得当前进程ID和父进程ID;
    for(;;);
}

关于进程的创建

fork()函数

函数失败时返回值是-1,返回值为0表示子进程逻辑控制流,如果返回值为其他,表示父进程控制流;
 函数的特点:
  1、调用一次,返回两次;
  2、相同但是具有独立的地址空间;
  3、可以并发执行;
  4、这个函数的本质是函数堆栈数据,text文本的复制;
函数的本质是父进程函数相关数据的复制,然后分叉出另一个函数;

#include <stdio.h>
#include <unistd.h>
int main(){
    printf("PID:%d,PPID:%d\n",getpid(),getppid());
    pid_t pid = fork();
    fork();
    if(pid == 0){// child
        printf("this is child\n");
        printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
    }else{
        printf("this is father\n");
        printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
    }
    for(;;);
}

子进程是父进程的副本,它将获得父进程数据空间、堆、栈等资源的副本。注意,子进程持有的是上述存储空间的“副本”,这意味着父子进程间不共享这些存储空间。
fork02.c:这个函数是用来构建子进程,用来输出一些值;

#include <stdio.h>
#include <unistd.h>
int i = 100;
int main(){
    int j=100;
    pid_t pid = fork();
    if(pid == 0){// child
        int k;
        for(k=0;k<10000;k++)
            printf("this is childi%d\t j%d\n",++i,++j);
    }else{
        int k;
        for(k=0;k<10000;k++)
            printf("this is fatheri%d\t j%d\n",--i,--j);
    }
}

fork03.c:这个函数父进程在增加变量的值,子进程在减小函数的值,用来模拟两个进程的运行情况;

#include <stdio.h>
#include <unistd.h>
int i = 100;
int main(){
    int j=100;
    FILE* fd = fopen("./test","w+");
    pid_t pid = fork();
    if(pid == 0){// child
        int k;
        for(k=0;k<10000;k++)
            fprintf(fd,"this is childi%d\t j%d\n",++i,++j);
    }else{
        int k;
        for(k=0;k<10000;k++)
            fprintf(fd,"this is fatheri%d\t j%d\n",--i,--j);
    }
}
exec函数簇
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(cosnt char *file, char *const argv[]);  
int execve(const char *path, char *const argv[], char *const envp[]);

execl.c

#include<stdio.h>
#include <unistd.h>

extern char ** environ;

int main(int argc,char **argv){
    execl("/bin/ps","ps","-a","-o","pid,ppid,cmd,stat",0);
    return 0;
}

execle.c

#include<stdio.h>
#include<unistd.h>

extern char **environ;

int main(int argc,char **argv){
    execle("/bin/ps","ps","-a","-o","pid,ppid,cmd,stat",0,environ);
    return 0;
}

execlp.c

#include <stdio.h>
#include <unistd.h>

int main(int argc,char **argv){
    execlp("ps","ps","-a","-o","pid,ppid,cmd,stat",0);
    return 0;
}

execv.c

#include <stdio.h>
#include <unistd.h>

int main(){
    char *args[]={"ps","-a","-o","pid,ppid,cmd,stat",0};
    execv("/bin/ps",args);
    return 0;
}

execve.c

#include <stdio.h>
#include <unistd.h>
extern char **environ;

int main(){
    char *args[]={"ps","-a","-o","pid,ppid,cmd,stat",0};
    execve("/bin/ps",args,environ);
    return 0;
}

execvp.c

#include<stdio.h>
#include<unistd.h>

int main(){
    char *argv[] = {"ps","-a","-o","pid,ppid,cmd,stat",0};
    execvp("ps",argv);
    return 0;
}

函数命名的规律组总结
v:表示第二个参数是数组;
l:第二个参数之后是变参;
p:第一个参数是文件名;
e:最后一个参数是环境变量;
常见的字符串数组函数
execv(),execvp(),execve()
可变参数函数
execle(),execlp(),execl();
exec.c函数:
 exec函数簇函数的返回值,-1表示失败,执行成功时,是不返回任何值的,这些函数具有以下特点:一次调用,失败返回;改朝换代,取而代之;但是函数的PID是不会变化的,变化的是程序地址空间的内容,exec函数簇函数的本质是覆盖原有函数,执行新的函数;

system(shell字符串)

这个函数是系统函数int system(shell 字符串或者命令);函数的返回值为-1时,表示失败;函数的返回值是127时,表示无法启动shell;其它返回值用于表示函数的退出码;系统调用函数一次调用,一次返回;这个函数本质上是在执行shell命令;
system.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>


int main(int argc,char** argv){
    printf("PID:%d\n",getpid());    
    system("sleep 3&");
    printf("PID:%d\n",getpid());    
}

进程的结束

对于return和exit来说,函数的执行属于寿终正寝,并且自己能够了结后事。_exit()函数属于自杀的情况,但是没有料理后事。abort()函数属于意外的死亡,也没有料理后事;信号终止属于他杀;料理后事表示的意思是程序结束以后,相关堆栈资源的回收工作;
 main()函数return 0 是语言级别的退出,使用这种情况的退出,函数仍然需要调用exit(0)函数,exit(0)是函数级别的退出;
 exit(0),表示释放所有的静态的全局对象,缓存,关掉所有的IO通道,然后终止程序;abort()会直接终止程序,但是不会释放任何资源;
 exit(0)函数在调用_exit()系统调用之前要检查文件的打开情况,吧文件缓冲区中的内容写回文件,清理IO缓冲,exit(0)是标准的C函数;
 _exit()不做清理IO缓冲函数,_exit()是Linux系统调用;
 main函数退出:
main.c

#include <stdio.h>
#include <unistd.h>
int main(){
    printf("PID:%d,PPID:%d\n",getpid(),getppid());
    
    return 100;
}

exit函数退出
exit函数一般用在main函数以外的函数退出

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
    printf("PID:%d,PPID:%d\n",getpid(),getppid());
    exit(EXIT_FAILURE);
    abort();
}

_exit函数一般用来结束子进程
这个函数暂时没有说明
abort()函数一般用来异常退出

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(){
    printf("PID:%d,PPID:%d\n",getpid(),getppid());
    abort();
}

还有一种方式--信号终止
pause.c

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void test(int sig){
    printf("revc a signal%d",sig);
}

int main(){
    signal(SIGINT,test);
    printf("before pause\n");
    pause();
    printf("after pause\n");
}

进程的停止

int sleep(unsigned int secs)函数 ,sece用于表示休眠的秒数,如果指定为-1表示永久休眠,函数的返回值是未休眠的秒数;如果没有信号中断,休眠指定的秒数返回0,否则马上返回未休眠的秒数;

#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <strings.h>
int main(){
    int i = 0;
    for(;;){
        time_t t;
        time(&t);
        struct tm* ptm =  gmtime(&t);
        char buf[BUFSIZ];
        bzero(buf,BUFSIZ);
        strftime(buf,BUFSIZ,"%P %T",ptm);
        printf("\r%s",buf);
        fflush(stdout);
        sleep(1);
    }
}

int pause()函数返回值一定是-1,如果函数没有处理信号,直接中断,执行默认的信号处理,程序后续代码不再执行。如果程序存在信号处理,执行处理信号后,执行后续代码。

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void test(int sig){
    printf("revc a signal%d",sig);
}
int main(){
    signal(SIGINT,test);
    printf("before pause\n");
    pause();
    printf("after pause\n");
}

pid_t wait(int * status);函数等价于pid_t waitpid(-1,status,0);这个函数的参数包括:pid:小于-1时,表示的是等待进程组为pid的所有进程,-1:表示等待任何子进程;0:表示等待同组的进程;大于0:表示等待进程为pid的子进程;
 status:参数的含义:
  正常结束,WIFEXITED(status),参数为0时,表示的是正常结束任何子进程,0表示的是非正常结束子进程;WEXITSTATUS()表示取得子进程exit()返回的结束代码,一般会先用WIFEXITED来判断是否正常结束才是用这个宏定义;
  异常结束:WIFSIGNALED(ststus)非0值表示异常结束子进程,0表示非异常结束子进程;WTERMSIG(status),取得子进程因信号而终止的信号代码,一般先会用WIFSIGNALED来判断后才能使用宏;
  暂停:WIFSTOPPED(status),非0值表示暂停结束子进程,0表示暂停结束子进程;WSTOPSIG(status),取得引发子进程暂停的信号代码,一般先用
WIFSTOPPED来判断后才能使用此宏;
 option,选项WNOHANG,若子进程没有结束,返回0,不予等待。若子进程结束,返回该子进程的ID;WUNTRACED,若子进程进入暂停状态,则马上返回,但子进程的结束状态不予理会。
 返回值,-1表示失败;其他值表示等待的PID;
wait.c:

#include <stdio.h>
#include <unistd.h>
#include <wait.h>

void handler(int sig){
    pid_t cpid = wait(NULL);
    printf("child %d exit",cpid);
}

int main(){
    signal(SIGCHLD,handler);
    printf("PID:%d,PPID:%d\n",getpid(),getppid());
    pid_t pid = fork();
    if(pid == 0){// child
        sleep(2);
        printf("this is child\n");
        printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
    }else{
        printf("this is father\n");
        printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
    }
}

wait02.c:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>

void handler(int sig){
    int status;
    pid_t cpid = wait(&status);
    if(WIFEXITED(status)){
        printf("child exit by %d\n",WEXITSTATUS(status));
    }   
    if(WIFSIGNALED(status)){
        printf("child exit by signal %d\n",WTERMSIG(status));
    }
    printf("child %d exit\n",cpid);
}

int main(){
    signal(SIGCHLD,handler);
    printf("PID:%d,PPID:%d\n",getpid(),getppid());
    pid_t pid = fork();
    if(pid == 0){// child
        sleep(2);
        printf("this is child\n");
        printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
        //exit(0);
    }else{
        printf("this is father\n");
        printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
        printf("leave:%d\n",sleep(5));
        //exit(200);
    }
    for(;;);
}

孤儿进程与僵尸进程

孤儿进程:父进程先于子进程退出,init进程作为新的父进程,但是对于系统无害,init进程会回收这些系统资源;
 僵尸进程:子进程退出,父进程没有获得子进程的状态信息,可以通过调用wait(status)或者调用waitpid函数来实现;
waitpid.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <wait.h>

int main(){
    printf("PID:%d,PPID:%d\n",getpid(),getppid());
    pid_t pid = fork();
    if(pid == 0){// child
        sleep(2);
        printf("this is child\n");
        printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
        //exit(0);
    }else{
        printf("this is father\n");
        printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
        printf("pid:%d exit\n",waitpid(pid,NULL,0));
    }
}

zomble.c:

#include <stdio.h>
#include <unistd.h>
#include <wait.h>
void handle(int sig){
    //wait(NULL);
    printf("this is child exit %d",sig);
}
int main(){
    signal(SIGCHLD,handle);
    printf("PID:%d,PPID:%d\n",getpid(),getppid());
    pid_t pid = fork();
    if(pid == 0){// child
        printf("this is child\n");
        printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
    }else{
        printf("this is father\n");
        printf("res:%d,PID:%d,PPID:%d\n",pid,getpid(),getppid());
        for(;;);
    }
}

一个模拟简单shell的程序:
shell.c

//这个程序是一个简单的shell程序,可以用于执行shell命令中不带有参数选项的命令;
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
 
void handle(int sig){
    wait(NULL);
}
int main(){
    printf("welcome to sim shell\n");
    //
    signal(SIGCHLD,handle);
 
    for(;;){
        printf(">");
        fflush(stdout);
        char buf[BUFSIZ];
        bzero(buf,BUFSIZ);
        fgets(buf,BUFSIZ,stdin);
            size_t n = strlen(buf);
        if(buf[n-1] == '\n'){
            buf[n-1] = '\0';
        }       
        if(strcmp(buf,"quit")==0)
            break;
        //system(buf);
        char* argv[128];
        bzero(argv,128);
        char* p = buf;
        int index = 0;
        char* temp = NULL;
        do{ 
            temp =strtok(p," ");
            p = NULL;
            argv[index++] = temp;
        }while(temp != NULL);
        if(fork() == 0){
            execvp(argv[0],argv);   
        }
        //sleep(-1);
        pause();
    }   
 
    printf("sim shell exit\n");
    return 0;
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,585评论 4 365
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,923评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,314评论 0 248
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,346评论 0 214
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,718评论 3 291
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,828评论 1 223
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,020评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,758评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,486评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,722评论 2 251
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,196评论 1 262
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,546评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,211评论 3 240
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,132评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,916评论 0 200
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,904评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,758评论 2 274

推荐阅读更多精彩内容

  • Linux 进程管理与程序开发 进程是Linux事务管理的基本单元,所有的进程均拥有自己独立的处理环境和系统资源,...
    JamesPeng阅读 2,406评论 1 14
  • 又来到了一个老生常谈的问题,应用层软件开发的程序员要不要了解和深入学习操作系统呢? 今天就这个问题开始,来谈谈操...
    tangsl阅读 4,013评论 0 23
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,105评论 18 139
  • 我一直觉得自己是个内向而敏感的人。 被别人伤了,想回击,可每次,反复思虑好几遍,最后还是放弃。人善被人欺,我总是这...
    唐菖蒲小朋友阅读 777评论 0 0
  • 如果依照苏格拉底和孔子思维来说 智者最好的行为就是(述而不作) 只是——面对这个世界混乱的世界 总得有人发一下声才...
    南岳衡山隐士阅读 520评论 0 0