系统编程一(进程基础)

本文详细介绍了进程的概念,从程序到进程的转变,重点讨论了ELF文件格式、虚拟存储器、进程状态以及进程操作接口,包括fork()、exec()和system()函数的使用。此外,还讲解了如何查看和结束进程,以及各种进程暂停方式,如休眠、暂停和等待,并探讨了特殊进程的相关知识。
摘要由CSDN通过智能技术生成

进程

如果程序是菜谱,进程就是厨师烹饪;
如果程序是乐谱,进程就是乐师演奏;
如果程序是剑谱,进程就是剑客舞剑;
如果程序是棋谱,进程就是棋士复盘;
程序是静的,进程是动的。

进程与程序区别

进程程序
动态静态
有生命周期指令集和
只能对应一个程序可以对应多个进程

概念

在这里插入图片描述

从代码到程序

在这里插入图片描述

从程序到进程

·内核将程序读入内存,为程序镜像分配内存空间。
·内核为该进程分配进程标志符PID。
·内核为该进程保存PID及相应的进程状态信息。
在这里插入图片描述

程序格式ELF

ELF(Executable and Linkable Format)文件格式,一种用于二进制文件、可执行文件、目标代码、共享库和核心转储格式文件。

查看程序(ELF文件):

readelf -S 文件名

查看进程空间大小:

size 文件名

虚拟存储器/虚拟地址空间

在这里插入图片描述
gdb查看内存映射信息:

info proc mapping

在这里插入图片描述

进程状态

在这里插入图片描述

状态含义
就绪(Ready)进程已获得到除CPU以外的所有必要的资源,获得CPU立即执行
运行(Running)程序正在CPU上执行
阻塞(Blocked)等待某个事件发生而无法执行时,放弃CPU

如何查看进程

在这里插入图片描述

ps命令

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

进程操作接口

获取PID

进程标识pid:进程身份证号

函数接口
pid_t getpid()获取当前进程ID
pid_t getppid()获取当前进程父进程ID
#include <stdio.h>
#include <unistd.h>
 
int main(){
    printf("PID:%dPPID:%d\n",getpid(),getppid());
}

如何查看进程的PID和PPID:

 ps -o pid,ppid,cmd,s

如何创建进程

1.分叉函数pid_t fork()
返回值含义
-1失败
0子进程逻辑控制流
其他(子进程PID)父进程逻辑控制流

特点:
1.调用一次,返回两次
2.相同但是独立的地址空间
3.并发执行
4.共享文件

示例
调用一次,返回两次:

#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(;;);
}

相同但是独立的地址空间&并发执行:

#include <stdio.h>
#include <unistd.h>
int i = 100;
int main(){
    int j=100;
    pid_t pid = fork();					//fork之后,i变量虽然地址相同,但是其实一分为二被父子两个
    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);
    }
}

共享文件:

#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);
    }
}

在这里插入图片描述

2.执行函数exec()

分类:

分类函数
字符串数组参数execv()、execvp()、execve()
可变参数execle()、execlp()、execl()

exec函数组名字规律:

字符含义
v第二个参数是数组
l第二个参数之后是变参
p第一个参数是文件名
e最后一个参数是环境变量

返回值

返回值含义
-1失败
不返回成功

在这里插入图片描述
本质:覆盖程序

示例:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
 
extern char ** environ;
 
int main(int argc,char** argv){
    printf("%s,PID %d\n",argv[0],getpid());
    //execl("/bin/ps","ps","-a","-o","pid,ppid,cmd",0);			//取命令的位置
    //execlp("ps","ps","-a","-o","pid,ppid,cmd",0);				//取命令文件名即可
    //execle("/bin/ps","ps","-a","-o","pid,ppid,cmd",0,environ);
    char* args[] = {"ps","-a","-o","pid,ppid,cmd",0};
    //execv("/bin/ps",args);
    execve("/bin/ps",args,environ);
    //execvp("ps",args);
    printf("%s,PID %d\n",argv[0],getpid());
}

注意:可以通过which查看命令的位置。

3.系统函数int system(Shell字符串)

返回值说明:

返回值含义
-1失败
127无法启动shell
其他命令退出码

特点: 一次调用,一次返回

本质: shell执行命令/程序

示例:

#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());    
}

Linux系统可以创建多少个进程?使用ulimit -a可以查看到。可以通过ulimit -u 进程数修改。
更详细的设置可以在/etc/security/limits.conf修改。

如何结束进程

方式说明
main函数退出只能用在main函数内
调用exit()函数一般用在main函数以外的函数
调用_exit()函数一般用来结束子进程
调用abort()函数一般用来异常退出
信号终止终止其他进程

示例:
return退出:

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

exit()/abort()退出:

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

如何停止进程

1.休眠
int sleep(unsigned int secs)

参数
secs指定休眠的秒数,-1表示永久休眠

返回值
未休眠的秒数

特性
如果没有信号中断,休眠指定秒数返回0,否则马上返回未休眠的秒数。

实现时钟:

#include<iostream>
#include<unistd.h>
#include<ctime>
using namespace std;

int main(){
        while(true){
                time_t t = time(NULL);
                tm* ptm = localtime(&t);
                char str[20]=[0];
                strftime(str,sizeof(str),"%Y/%m/%d %T",ptm);
                cout << '\r' << str << flush;		//每次刷新后光标回到行首
                sleep(1);			//睡眠一秒后刷新
        }
}

实现进度条:

#include<iostream>
#include<unistd.h>
#include<iomanip>
using namespace std;

int main(){
        for(int i=0;i<=100;++i){
                cout << '\r' << setw(2) << i << '%';		//刷新数字为两位
                for(int j=0;j<=i;++j){
                        cout << "=";
                }
                cout << flush;
                usleep(100000);		//usleep进程的睡眠单位为微秒
        }
        cout << endl;
}
2.暂停
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");
}
3.等待
pid_t wait(int* status);		//等价于pid_t waitpid(-1,stauts,0)

pid_t waitpid(pid_t pid,int * status,int options);

参数说明:

参数说明
pid<-1:等待进程组为pid的所有进程;-1: 等待任何子进程;0:等待同组的进程;>0:进程为pid 的子进程
status判断正常结束:使用WIFEXITED(status);判断异常结束使用WIFSIGNALED(status);判断暂停使用WIFSTOPPED(status)
optionsWNOHANG若子进程没有结束,返回0,不予以等待;若子进程结束,返回该子进程的ID。WUNTRACED若子进程进入暂停状态,则马上返回,但子进程的结束状态不予以理会

正常结束:WIFEXITED(status)

参数说明
非0正常结束子进程
0非正常结束子进程

WEXITSTATUS(status)取得子进程exit()返回的结束代码,一般会先用WIFEXITED来判断是否正常结束才能使用此宏

异常结束:WIFSIGNALED(status)

参数说明
非0异常结束子进程
0非异常结束子进程

WTERMSIG(status)取得子进程因信号而中止的信号代码,一般会先用 WIFSIGNALED 来判断后才使用此宏

暂停:WIFSTOPPED(status)

参数说明
非0暂停结束子进程
0非暂停结束子进程

WSTOPSIG(status)取得引发子进程暂停的信号代码,一般会先用 WIFSTOPPED 来判断后才使用此宏。

返回值说明:

返回值说明
-1失败
其他等到的pid

示例
父进程等待子进程退出:

#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));
    }
}

更加安全的方式:

#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(;;);
}

在这里插入图片描述

特殊进程

在这里插入图片描述
示例:

#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:


#include<iostream>
#include<sstream>
#include<vector>
#include<unistd.h>
using namespace std;

int main(){
        for(::){
                //从终端读取一行
                string line;
                getline(cin,line);

                //把命令行按照空白符分割
                istringstream iss(line);
                vector<string> cmd;
                string option;
                while(iss >> option){
                        cmd.push_back(option);
                }
                const char* opts[cmd.size()+1] = {NULL};
                int i = 0;
                for(auto& c:cmd)
                        opts[i++] = c.c_str();
                if(0 == fork()){
                        if(-1==execvp(opts[0].const_cast<char* const>(opts))){
                                perror("execute cmd error");
                        }
                }
        }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值