美文网首页
Linux 进程

Linux 进程

作者: 攻城老狮 | 来源:发表于2021-05-28 08:15 被阅读0次

    一 环境变量

    1 常见环境变量

    #/home/ypw/bin:/home/ypw/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
    PATH 
    # /home/ypw
    HOME 
    # /bin/bash
    SHELL 
    # xterm-256color
    TERM 
    # en_US.UTF-8
    LANG
    

    2 程序获取环境变量

    #include<stdio.h>
    //导入存放环境变量的数组
    extern char** environ;
    
    int main(){
        //NULL作为哨兵结尾
        for(int i=0 ; environ[i]!=NULL ; i++){
            printf("%s\n",environ[i]);
        }
        return 0;
    }
    

    3 函数操作环境变量

    • 函数文档
    char *getenv(const char *name);
    //overwrite不为0重写
    int setenv(const char *name, const char *value, int overwrite); 
    int unsetenv(const char *name);
    
    #include<stdio.h>
    #include<stdlib.h>
    // 获取环境变量,添加环境变量,删除环境变量
    int main(){
        //获取环境变量
        const char* name = "VAR";
        char* val = getenv(name);
        printf("%s=%s\n",name,val);
        //添加环境变量
        setenv(name,"hello world",1);
        //获取环境变量
        val = getenv(name);
        printf("%s=%s\n",name,val);
        //删除环境变量
        int ret = unsetenv(name);
        printf("%d\n",ret);
        return 0;
    }
    

    二 进程控制

    1 fork函数

    • 使用fork创建子进程
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<sys/types.h>
    // 使用fork创建子进程,子进程的fork()返回值为0,父进程的fork()返回值为子进程pid
    int main(){
        pid_t pid;
        printf("---before fork---\n");
        pid = fork();
        if(pid == -1){
            perror("fork:");
            exit(1);
        }else if(pid == 0){
            printf("child process: pid:%u,ppid:%u\n",getpid(),getppid());
        }else{
            sleep(1);
            printf("parent process: pid:%u,ppid:%u\n",getpid(),getppid());
        }
        printf("---after fork---\n");
        return 0;
    }
    
    • 使用fork循环创建子进程
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<sys/types.h>
    # 使用fork循环创建子进程
    int main(){
        pid_t pid;
        printf("--before fork--\n");
        int i;
        for(i=0 ; i<5 ; i++){
            pid = fork();
            if(pid == -1){
                perror("fork: ");
                exit(1);
            }else if(pid == 0){
                 // 避免子进程再创建子进程的子进程,需要跳出循环,表示子进程不再fork
                break;
            }
        }
        // 子进程
        if(i<5){
            sleep(i);
            printf("%dth child process ,pid=%u\n",i+1,getpid());
        }else{ // 父进程
            sleep(i);
            printf("parent process,pid=%u\n",getpid());
        }
    
        return 0;
    }
    
    # result
    --before fork--
    1th child process ,pid=60180
    2th child process ,pid=60181
    3th child process ,pid=60182
    4th child process ,pid=60183
    5th child process ,pid=60184
    parent process,pid=60179
    
    • 进程共享

    读时共享写时复制

    • gdb调试带有子进程的程序
    # 默认跟踪父进程,在fork()函数调用之前设置,可以修改gdb跟踪的进程
    set follow-fork-mode child
    set follow-fork-mode parent
    

    2 exec函数族

    当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec不会创建新进程,所有调用exec前后该进程的id未被改变。

    exec函数一旦调用成功即立即执行新的程序,不返回。只有失败才返回,错误值为-1。故通常在exec函数调用后直接调用perror()和exit(),无需if判断

    /*
        l(list):命令行参数列表
        p(path):搜索file时,使用path变量
        v(vector):使用命令行参数数组
        e(environment):使用环境变量数组
    */
    int execl();
    int execlp();
    int execle();
    int execv();
    int execvp();
    int execve();
    
    • execlp函数(加载一个进程,借助PATH环境变量)
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    // 在子进程中执行ls命令
    int main(){
        pid_t pid;
        //创建子进程
        pid = fork();
        if(pid == -1){
            perror("fork");
            exit(1);
        }else if(pid == 0){
            //子进程执行execlp函数
            execlp("ls","ls","-l","-a",NULL);
        }else{
            sleep(1);
            printf("parent\n");
        }
        return 0;
    }
    
    • execl函数 (加载一个进程,通过 路径+函数名 加载)
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    // 在子进程中执行ls命令
    int main(){
        pid_t pid;
        //创建子进程
        pid = fork();
        if(pid == -1){
            perror("fork");
            exit(1);
        }else if(pid == 0){
            //子进程执行execl函数,可以执行自定义的程序
            execl("/bin/ls","ls","-l","-a",NULL);
        }else{
            sleep(1);
            printf("parent\n");
        }
        return 0;
    }
    
    • execlp函数实现将当前系统的进程信息打印到文件中
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<fcntl.h>
    //将当前系统的进程信息打印到文件中
    int main(){
        //打开文件,只写,没有创建,有则截断
        int fd = open("ps.out",O_WRONLY|O_CREAT|O_TRUNC,0644);
        if(fd == -1){
            perror("open");
            exit(1);
        }
        //将fd的文件描述符复制到标准输出,标准输出写入到fd指向的文件
        dup2(fd,1);
        //执行ps
        execlp("ps","ps","aux",NULL);
        //成功不返回
        perror("ps");
        exit(1);
        //close(fd);
        return 0;
    }
    

    三 回收子进程

    1 孤儿进程

    孤儿进程是表示父进程先于子进程结束,则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程。

    由于我使用的环境是带有图形界面的ubuntu系统,所以最终并不是被我们所熟知的init进程收养,而是被一个名为/sbin/upstart的进程所收养。另外还可以观察到,该进程也是其他系统进程的父进程。

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    //当父进程死亡后,子进程成为孤儿进程,被1685进程收养
    int main(){
        pid_t pid;
        pid = fork();
        if(pid == -1){
            perror("fork");
            exit(1);
        }else if(pid == 0){
            while(1){
                sleep(1);
                printf("child process pid=%u,ppid=%u\n",getpid(),getppid());
            }
        }else{
            sleep(3);
            printf("parent process pid=%u,childPid=%u\n",getpid(),pid);
        }
        return 0;
    }
    

    2 僵尸进程

    僵尸进程表示当进程终止后,父进程尚未回收,子进程残留资源(PCB)存放在内核中,变为僵尸进程。僵尸进程不能使用kill命令清除,因为kill只是用来终止进程的,而僵尸进程已经终止。

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    
    int main(){
        pid_t pid;
        pid = fork();
        if(pid == -1){
            perror("fork");
            exit(1);
        }else if(pid == 0){
                sleep(3);
                printf("child process pid=%u,ppid=%u\n",getpid(),getppid());
        }else{
            while(1){
                sleep(1);
                printf("parent process pid=%u,childPid=%u\n",getpid(),pid);
            }
        }
        return 0;
    }
    
    # result
    ypw   62275  0.0  0.0   4352   656 pts/2    S+   19:46   0:00 ./zoombie
    ypw   62276  0.0  0.0      0     0 pts/2    Z+   19:46   0:00 [zoombie] <defunct>
    

    3 wait 函数

    父进程调用wait函数可以回收子进程终止信息。三个功能:

    • 阻塞等待子进程退出
    • 回收子进程残留资源
    • 获取子进程结束状态(退出原因)
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/wait.h>
    
    int main(){
        
        pid_t pid,wpid;
        pid = fork();
        if(pid == -1){
            perror("fork");
            exit(1);
        }else if(pid == 0){
                sleep(3);
                printf("child process pid=%u,ppid=%u\n",getpid(),getppid());
        }else{
            wpid = wait(NULL); //阻塞,等待子进程结束,回收该僵尸进程,成功返回子进程的pid
            if(wpid == -1){
                perror("wait error");
                exit(1);
            }
            while(1){
                sleep(1);
                printf("parent process pid=%u,childPid=%u\n",getpid(),pid);
            }
        }
        return 0;
    }
    

    增强版,可以获取进程的状态信息

    WIFEXITED(status):非零,进程正常结束
    WEXITSTATUS(status):上面的宏为真,则使用该宏可以获取进程退出状态 (exit/return 的状态)
    WIFSIGNALED(status):非零,进程异常终止
    WTERMSIG(status):取得使进程终止的那个信号的编号 (例:kill -9 pid 获取到编号为9)
    
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/wait.h>
    
    int main(){
        
        pid_t pid,wpid;
        int status;
        pid = fork();
        if(pid == -1){
            perror("fork");
            exit(1);
        }else if(pid == 0){
                sleep(30);
                printf("child process pid=%u,ppid=%u\n",getpid(),getppid());
                exit(76); // return 100;
        }else{
            wpid = wait(&status);
            if(WIFEXITED(status)){
                printf("return or exit success: %d\n",WEXITSTATUS(status));
            }
            if(WIFSIGNALED(status)){
                printf("kill by the number: %d\n",WTERMSIG(status));
            }
            if(wpid == -1){
                perror("wait error");
                exit(1);
            }
            while(1){
                sleep(1);
                printf("parent process pid=%u,childPid=%u\n",getpid(),pid);
            }
        }
        return 0;
    }
    

    4 waitpid 函数

    作用同wait,但可以指定清理的pid进程,可以非阻塞

    // 参数:pid:>0 回收指定ID的子进程,-1 回收任意子进程(同wait)
    // 返回:0,参数3为宏 WNOHANG,表示子进程正在运行
    pid_t waitpid(pid_t pid, int *status, int options);
    
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/wait.h>
    
    int main(){
    
        pid_t pid,pid_three,wpid;
        int i,n=5;
        for(i=1 ; i<=n ; i++){
            pid = fork();
            if(pid == -1){
                perror("fork");
                exit(1);
            } else if(pid == 0 ){
                break;
            } else{
                if(i==3){ //当fork第三个进程时候,记录进程号
                    pid_three = pid;
                }
            }
        }
        
        if(i == n+1){
            sleep(i);
    //      pid_three = waitpid(pid_three,NULL,0); //阻塞回收第三个进程
    //      printf("waitpid=%d\n",pid_three);
            do{ //循环回收全部进程
                wpid = waitpid(-1,NULL,WNOHANG); //非阻塞式
                if(wpid>0){
                    n--;
                }
            }while(n>0);
            printf("wait finish...\n");
            printf("parent id=%d\n",getpid());
            while(1);
        }else{
            sleep(i);
            printf("child%d id=%d\n",i,getpid());
        }
        return 0;
    }
    

    四 进程间通信

    1 Pipe管道

    //功能:使用pipe实现,父子进程通信,父写数据,子读数据
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/wait.h>
    #include<string.h>
    
    void sys_err(const char* str,int errno){
        perror(str);
        exit(errno);
    }
    
    int main(){
    
        int fd[2];
        pid_t pid;
        char str[1024] = "Hello World\n";
        char buf[1024];
        int len;
    
        if(pipe(fd)<0){ //创建管道 fd[0]读端 fd[1]写端
            sys_err("pipe error",1);
        }
        
        pid = fork(); //创建子进程
        if(pid == -1){
            sys_err("fork error",1);
        }else if(pid > 0){ //父进程
            close(fd[0]); //父关闭读
            write(fd[1],str,strlen(str)); //向管道写数据
            wait(NULL); //回收子进程
            close(fd[1]); 
        }else{ //子进程
            close(fd[1]); //子关闭写
            len = read(fd[0],buf,sizeof(buf)); //从管道读数据
            write(STDOUT_FILENO,buf,len); //将数据写到屏幕
            close(fd[0]);               
        }
        return 0;
    }
    

    2 fifo有名管道

    # 借助fifo实现非血缘进程间通信
    mkfifo myfifo
    
    //扮演写的程序
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<string.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<fcntl.h>
    
    void sys_err(const char* str,int errno){
        perror(str);
        exit(errno);
    }
    
    int main(){
        
        int fd;
        char* str = "Hello Fifo";
        fd = open("myfifo",O_WRONLY); //打开fifo管道
        if(fd == -1 ){
            sys_err("open error",1);
        }
        write(fd,str,strlen(str)); //向管道写数据
        close(fd);
        return 0;
    }
    
    //扮演读的程序
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<string.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<fcntl.h>
    
    void sys_err(const char* str,int errno){
        perror(str);
        exit(errno);
    }
    
    int main(){
        
        int fd;
        char buf[1024];
        int len;
    
        fd = open("myfifo",O_RDONLY); //打开fifo管道
        if(fd == -1 ){
            sys_err("open error",1);
        }
        len = read(fd,buf,sizeof(buf)); //读数据
        write(STDOUT_FILENO,buf,len); 
        close(fd);
        return 0;
    }
    

    3 mmap内存共享映射

    //借助mmap实现非血缘关系进程的通信
    //扮演写的程序
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<fcntl.h>
    #include<sys/mman.h>
    #include<string.h>
    #include<unistd.h>
    
    #define MAPLEN 0x1000
    
    struct Stu{ //定义传输的数据格式为结构体
        int id;
        char name[20];
        char sex;
    };
    
    void sys_err(const char* str, int errno){
        perror(str);
        exit(errno);
    }
    
    int main(int argc,char* argv[]){
    
        int fd,i=0;
        struct Stu* mm;
    
        if(argc < 2){
            printf("./a.out filename");
            exit(1);
        }
    
        fd = open(argv[1],O_RDWR | O_CREAT, 0777); //有则打开无则创建
        if(fd < 0 ){
            sys_err("open error",1);
        }
    
        if(lseek(fd,MAPLEN-1,SEEK_SET)<0){ //提供4KB大写的空文件
            sys_err("lseek error",2);
        }
        if(write(fd,"\0",1)<0){ //lseek写入空洞字符后,需要再追加写入一个字符
            sys_err("write error",3);
        }
        //内存共享映射
        mm = (struct Stu*)mmap(NULL,MAPLEN,PROT_READ | PROT_WRITE, MAP_SHARED,fd,0);
        if(mm == MAP_FAILED){ //映射失败
            sys_err("mmap error",4);
        }
        close(fd); //可以关闭文件描述符,已经建立映射故不影响
    
        while(1){ //循环向共享内存中写数据
            mm->id = i;
            sprintf(mm->name,"stu-%d",i);
            if(i%2==0)
                mm->sex = 'm';
            else
                mm->sex = 'w';
            i++;
            sleep(1);
        }
        munmap(mm,MAPLEN); //解绑映射
        return 0;
    }
    
    
    //扮演读的程序
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<fcntl.h>
    #include<sys/mman.h>
    #include<string.h>
    #include<unistd.h>
    
    #define MAPLEN 0x1000
    
    struct Stu{
        int id;
        char name[20];
        char sex;
    };
    
    void sys_err(const char* str, int errno){
        perror(str);
        exit(errno);
    }
    
    int main(int argc,char* argv[]){
    
        int fd,i=0;
        struct Stu* mm;
    
        if(argc < 2){
            printf("./a.out filename");
            exit(1);
        }
    
        fd = open(argv[1],O_RDONLY); //只读
        if(fd < 0 ){
            sys_err("open error",1);
        }
    
        mm = (struct Stu*)mmap(NULL,MAPLEN,PROT_READ , MAP_SHARED,fd,0);
        if(mm == MAP_FAILED){
            sys_err("mmap error",4);
        }
        close(fd);
    
        while(1){ //循环读取映射共享的数据
            printf("id:%d ",mm->id);
            printf("name:%s ",mm->name);
            printf("sex:%c\n",mm->sex);
            sleep(1);
        }
        munmap(mm,MAPLEN);
        return 0;
    }
    

    五 信号

    1 kill函数

    //使用kill函数模拟系统的kill命令,向目标进程pid发送信号
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<signal.h>
    int main(int argc,char* argv[]){
        
        int ret;
    
        if(argc < 3){
            printf("./a.out signalNumber pid");
            exit(1);
        }
        
        ret = kill((pid_t)atoi(argv[2]),atoi(argv[1])); //发送信号,pid,signal
        if(ret < 0 ){
            perror("kill");
            exit(1);
        }
        return 0;
    }
    

    2 alarm函数

    //定时器,运行规定的时间后自动终止进程
    #include<stdio.h>
    #include<unistd.h>
    
    int main(){
    
        int count;
    
        alarm(1); //定时1s
        for(count = 0 ; 1 ; count++){
            printf("count=%d\n",count);
        }
        return 0;
    }
    

    3 信号集处理函数

    //通过更改屏蔽字,屏蔽相应的信号
    #include<stdio.h>
    #include<signal.h>
    #include<unistd.h>
    
    void printsigset(const sigset_t* set){ //打印当前未决信号集
        int i;
        for(i=1 ; i<32 ; i++){
            if(sigismember(set,i)==1){ //判断i信号是否在未决信号集上激活
                putchar('1');
            }else{
                putchar('0');
            }
        }
        puts("");
    }
    
    int main(){
        
        sigset_t s,p;
        int i=0;    
        sigemptyset(&s); //置零
        sigaddset(&s,SIGINT); //SIGINT信号阻塞置1
        sigaddset(&s,SIGQUIT); //SIGQUIT信号阻塞置1
        sigprocmask(SIG_BLOCK,&s,NULL); //使用s来更改当前进程的信号屏蔽字
    
        while(1){
            sigpending(&p); //获取未决信号集
            printsigset(&p);
            sleep(1);
            if(i == 10){
                sigdelset(&s,SIGQUIT); //删除阻塞信号集的SIGQUIT信号,置零
                sigprocmask(SIG_UNBLOCK,&s,NULL); //将SIGINT信号置零放行SIGINT未决信号
            }
            i++;
        }   
    
        return 0;
    }
    

    4 信号捕捉

    • 给SIGINT信号设置捕捉函数
    #include<stdio.h>
    #include<signal.h>
    #include<unistd.h>
    
    void do_sig(int num){ //捕捉函数
        printf("I am do_sig function\n");
        printf("num=%d\n",num);
    }
    
    int main(){
    
        struct sigaction act; //定义捕捉的结构体
    
        act.sa_handler = do_sig; //添加捕捉函数
        sigemptyset(&act.sa_mask); //临时信号屏蔽字
        act.sa_flags = 0; //选定旧版本捕捉函数
    
        sigaction(SIGINT,&act,NULL); //给SIGINT信号添加捕捉函数
    
        while(1){
            printf("=============\n");
            sleep(1);
        }
    
        return 0;
    }
    
    • 验证临时信号屏蔽字
      • 当在执行捕捉函数时,再发送SIGINT信号,则会只改变未决信号集置1,等待捕捉函数执行完毕,临时屏蔽字解除后,会被激活
      • 当在执行捕捉函数时,由于设置了对SIGQUIT信号的屏蔽,故执行阶段无法响应SIGQUIT信号
    #include<stdio.h>
    #include<signal.h>
    #include<unistd.h>
    
    void do_sig(int num){
        int n=5;
        printf("I am do_sig function\n");
        while(n--){ //添加循环,增加捕捉函数的执行时间
            printf("num=%d\n",num);
            sleep(1);
        }
    }
    
    int main(){
    
        struct sigaction act;
    
        //act.sa_handler = SIG_DFL; //默认
        //act.sa_handler = SIG_IGN; //忽略
        act.sa_handler = do_sig;
        sigemptyset(&act.sa_mask);
        sigaddset(&act.sa_mask,SIGQUIT); //对SIGQUIT信号临时屏蔽
        act.sa_flags = 0;
    
        sigaction(SIGINT,&act,NULL);
    
        while(1){
            printf("=============\n");
            sleep(1);
        }
    
        return 0;
    }
    

    5 signal 函数

    //捕捉SIGUSR1信号
    #include<stdio.h>
    #include<signal.h>
    #include<unistd.h>
    
    void do_sig(int num){
        printf("num=%d\n",num);
    }
    
    int main(){
        
        signal(SIGUSR1,do_sig);
        
        while(1){
            printf("***********\n");
            sleep(1);
        }
        
        return 0;
    }
    

    6 可重入函数

    //将不可重入函数strtok更改为可重入函数strtok_r,用于对字符串的切割
    #include<stdio.h>
    #include<string.h>
    
    int main(){
        
        char buf[] = "Hello Tom Jerry Smith";
        char *save = buf, *p;
    
        while((p = strtok_r(save," ",&save))!=NULL){
            printf("%s\n",p);
        }
    
        return 0;
    }
    

    7 时序京态

    //自己实现sleep函数,需要考虑时序静态的发生,在alarm前,需要设置信号屏蔽字来屏蔽SIGALRM信号,之后再解除SIGALRM信号的阻塞,通过sigsuspend函数原子化操作实现信号唤醒挂起
    #include<stdio.h>
    #include<signal.h>
    #include<unistd.h>
    
    void do_sig(int num){
        //nothing to do
    }
    
    unsigned int mysleep(unsigned int nsecs){
        struct sigaction act,oldact;
        sigset_t newmask,oldmask,susmask;
        unsigned int unslept;
        //给SIGALRM信号设置捕捉函数
        act.sa_handler = do_sig; 
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGALRM,&act,&oldact);
        //设置信号屏蔽字,屏蔽SIGALRM信号
        sigemptyset(&newmask);
        sigaddset(&newmask,SIGALRM);
        sigprocmask(SIG_BLOCK,&newmask,&oldmask);
        //定时器
        alarm(nsecs);
        //解除SIGALRM信号的屏蔽
        susmask = oldmask;
        sigdelset(&susmask,SIGALRM);
        sigsuspend(&susmask); //挂起等待SIGALRM信号幻醒
    
        unslept = alarm(0);
        sigaction(SIGALRM,&oldact,NULL); //恢复SIGALRM信号默认响应状态
        sigprocmask(SIG_SETMASK,&oldmask,NULL); //恢复信号屏蔽字
        return unslept;
    }
    
    
    int main(){
        while(1){
            mysleep(2);
            printf("Two seconds passed\n");
        }
    
        return 0;
    }
    

    SIGCHLD信号处理

    //通过对信号的处理实现清理僵尸进程
    #include<stdio.h>
    #include<stdlib.h>
    #include<signal.h>
    #include<sys/types.h>
    #include<sys/wait.h>
    #include<unistd.h>
    
    
    void sys_err(const char* str,int errno){
        perror(str);
        exit(errno);
    }
    //捕捉函数
    void do_sig_child(int num){
        int status;
        pid_t pid;
        while((pid = waitpid(0,&status,WNOHANG))>0){ //waitpid 处理一个僵尸进程
            if(WIFEXITED(status)){ //正常退出
                printf("child %d exit %d\n",pid,WEXITSTATUS(status));
            }else if(WIFSIGNALED(status)){ //异常退出
                printf("childe %d cancel signal %d\n",pid,WTERMSIG(status));
            }
        }
    }
    
    int main(){
    
        pid_t pid;
        int i;
        for(i=0 ; i<10 ; i++){ //fork 10个子进程
            pid = fork();
            if(pid < 0) {
                sys_err("fork",1);
            }else if(pid == 0 ){
                break;
            }
        }
    
        if(pid == 0 ){ //子进程
            int n = 15;
            while(n--){
                sleep(1);
                printf("child pid %d\n",getpid());
            }
            return i; //返回退出的值
        }else if(pid > 0){ //父进程
             //设置捕捉SIGCHLD信号
            struct sigaction act;
            act.sa_handler = do_sig_child;
            sigemptyset(&act.sa_mask);
            act.sa_flags = 0;
            sigaction(SIGCHLD,&act,NULL);
            while(1){
                sleep(1);
                printf("parent pid %d\n",getpid());
            }
        }
    
        return 0;
    }
    

    六 进程间关系

    1 进程组

    //获取进程组gid的几种方式
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    
    int main(){
    
        pid_t pid;
    
        if((pid = fork())<0){
            perror("fork");
            exit(1);
        }else if(pid == 0) {
            printf("child pid:%d\n",getpid());
            printf("child pgid:%d\n",getpgrp()); //获取组id
            printf("child pgid:%d\n",getpgid(0));//获取组id
            printf("child pgid:%d\n",getpgid(getpid()));//获取组id
        }else{  
            sleep(3);
            printf("parent pid:%d\n",getpid());
            printf("parent pgid:%d\n",getpgid(0));//获取组id
        }
        return 0;
    }
    
    //改变进程的进程组id
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    int main(void)
    {
        pid_t pid;
        if ((pid = fork()) < 0) {
            perror("fork");
            exit(1);
        } else if (pid == 0) {
            printf("child process PID is %d\n",getpid());
            printf("Group ID of child is %d\n",getpgid(0)); // 返回组id
            sleep(5);
            printf("Group ID of child is changed to %d\n",getpgid(0));
            exit(0);
        }
        sleep(1);
        setpgid(pid,pid); // 父进程改变子进程的组id为子进程本身
        sleep(5);
        printf("parent process PID is %d\n",getpid());
        printf("parent of parent process PID is %d\n",getppid());
        printf("Group ID of parent is %d\n",getpgid(0));
        setpgid(getpid(),getppid()); // 改变父进程的组id为父进程的父进程
        printf("Group ID of parent is changed to %d\n",getpgid(0));
        return 0;
    }
    

    2 会话设置

    //子进程创建独立的会话,关闭当前终端不影响该会话的正常运行
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    
    int main(){
    
        pid_t pid;
    
        if((pid = fork())<0){
            perror("fork");
            exit(1);
        }else if(pid == 0 ){
            printf("child pid:%d\n",getpid());
            printf("child pgid:%d\n",getpgid(0));
            printf("child sid:%d\n",getsid(0));
            sleep(10);
            setsid(); //子进程非组长进程,故其成为新会话首进程,且成为组长进程。该进程组id即为会话进程
            printf("Changed\n");
            printf("child pid:%d\n",getpid());
            printf("child pgid:%d\n",getpgid(0));
            printf("child sid:%d\n",getsid(0));
            sleep(20);  
            exit(0);
        }
    
        return 0;
    }
    

    七 线程

    1 pthread_create 创建线程

    //创建线程,打印一些线程信息
    #include<stdio.h>
    #include<pthread.h>
    #include<stdlib.h>
    #include<unistd.h>
    
    void* do_thread_func(void* arg){
        int* p = (int*)arg;
        printf("thread running...\n");
        printf("thread pid:%d\n",getpid());
        printf("thread id:%x\n",(unsigned int)pthread_self());
        printf("thread arg:%d\n",*p);
    }
    
    int main(){
        
        pthread_t tid;
        int arg = 233;  
        
        pthread_create(&tid,NULL,do_thread_func,(void *)&arg); //创建线程
        
        printf("main pid:%d\n",getpid());
        printf("main thread id:%x\n",(unsigned int)pthread_self()); //main线程的tid
        printf("main child thread id:%x\n",(unsigned int)tid); //新线程的tid
        sleep(1);   
        return 0;
    }
    

    2 pthread_join 回收线程

    //回收线程
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<pthread.h>
    
    void* do_thread_func1(void* arg){
        printf("thread 1 running...\n");
        return (void*)1;
    }
    
    void* do_thread_func2(void* arg){
        printf("thread 2 running...\n");
        pthread_exit((void*)2);
    }
    
    void* do_thread_func3(void* arg){
        while(1){
            printf("thread 3 writing...\n");
            sleep(1);
        }
    }
    
    int main(){
    
        pthread_t tid;
        void* retval;
    
        pthread_create(&tid,NULL,do_thread_func1,NULL);
        pthread_join(tid,&retval);
        printf("thread 1 exit code:%d\n",(int*)retval);
    
        pthread_create(&tid,NULL,do_thread_func2,NULL);
        pthread_join(tid,&retval);  
        printf("thread 2 exit code:%d\n",(int*)retval);
        
        pthread_create(&tid,NULL,do_thread_func3,NULL);
        sleep(3);   
        pthread_cancel(tid);
        pthread_join(tid,&retval);
        printf("thread 3 exit code:%d\n",(int*)retval);
    
        return 0;
    }
    

    3 pthread_detached 分离线程

    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    #include<pthread.h>
    #include<unistd.h>
    
    void* do_thread_func(void* arg){
        int n=3;
        while(n--){
            printf("thread running...\n");
            sleep(1);
        }
        return (void*)233;
    }
    
    int main(){
    
        pthread_t tid;
        int err;
        void *retval;
            
        pthread_create(&tid,NULL,do_thread_func,NULL);
        pthread_detach(tid); //分离线程,无法使用join回收,无法接收返回的参数
        while(1){
            err = pthread_join(tid,&retval);
            if(err != 0 ){
                printf("thread %s\n",strerror(err));
            }else{
                printf("thread exit code %d\n",(int)retval);
            }
            sleep(1);   
        }
    
        return 0;
    }
    

    4 设置线程属性

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<pthread.h>
    #include<string.h>
    
    #define SIZE 0x100000
    
    void* do_thread_func(void* arg){
        int n = 3;
        while(n--){
    //      printf("Hello thread\n");
            sleep(1);
        }
    }
    
    int main(){
    
        pthread_t tid;
        pthread_attr_t attr;
        int err,detachstate,i=1;
        void* stackaddr;
        size_t stacksize;
        
        pthread_attr_init(&attr);
    
        pthread_attr_getstack(&attr,&stackaddr,&stacksize);
        printf("origin stackaddr:%p\n",stackaddr);
        printf("origin stacksize:%d\n",(int)stacksize);
        
        pthread_attr_getdetachstate(&attr,&detachstate);
        if(detachstate == PTHREAD_CREATE_DETACHED){
            printf("thread detached\n");
        }else if(detachstate == PTHREAD_CREATE_JOINABLE){
            printf("thread joinable\n");
        }
        
        pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
        while(1){
            stackaddr = malloc(SIZE);
            if(stackaddr == NULL){
                perror("malloc");
                exit(1);
            }
            stacksize = SIZE;
            pthread_attr_setstack(&attr,stackaddr,stacksize);
            err = pthread_create(&tid,&attr,do_thread_func,NULL);
            if(err != 0 ){
                printf("thread:%s\n",strerror(err));
                exit(1);
            }
            printf("%d\n",i++);
        }
        
        pthread_attr_destroy(&attr);
        return 0;
    }
    

    八 线程同步

    1 互斥量

    //通过互斥量mutex实现对临界区资源count的加锁访问,比卖你出现并发问题
    #include<stdio.h>
    #include<pthread.h>
    
    #define LOOPNUM 5000
    int count;
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //互斥量静态初始化
    
    void* do_thread_func(void* arg){
        int i,val;
        for(i=0 ; i<LOOPNUM ; i++){
            pthread_mutex_lock(&mutex); //加锁访问临界资源
            val = count;
            printf("%x:%d\n",(unsigned int)pthread_self(),val+1);
            count = val+1;
            pthread_mutex_unlock(&mutex); //解锁临界资源
        }
        return NULL;
    }
    
    int main(){
    
        pthread_t tid1,tid2;
        
        pthread_create(&tid1,NULL,do_thread_func,NULL);
        pthread_create(&tid2,NULL,do_thread_func,NULL);
    
        pthread_join(tid1,NULL);
        pthread_join(tid2,NULL);
    
        return 0;
    }
    

    2 死锁

    //线程一拥有A锁,请求获得B锁;线程二拥有B锁,请求获得A锁
    #include<stdio.h>
    #include<pthread.h>
    #include<unistd.h>
    
    char* str = "Hello World";
    pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER;
    
    void* do_thread_func1(void* arg){
        pthread_mutex_lock(&lock1);
        sleep(1);
        pthread_mutex_lock(&lock2);
        printf("%s\n",str);
        pthread_mutex_unlock(&lock2);
        pthread_mutex_unlock(&lock1);
        return NULL;
    }
    
    void* do_thread_func2(void* arg){
        pthread_mutex_lock(&lock2);
        sleep(2);
        pthread_mutex_lock(&lock1);
        printf("%s\n",str);
        pthread_mutex_unlock(&lock1);
        pthread_mutex_unlock(&lock2);
        return NULL;
    }
    
    int main(){
    
        pthread_t tid1,tid2;
    
        pthread_create(&tid1,NULL,do_thread_func1,NULL);
        pthread_create(&tid2,NULL,do_thread_func2,NULL);
    
        pthread_join(tid1,NULL);
        pthread_join(tid2,NULL);
        return 0;
    }
    

    3 读写锁

    //3个线程不定时写同一全局资源,5个线程不定时读同一全局资源
    #include<stdio.h>
    #include<pthread.h>
    #include<unistd.h>
    
    int count;
    pthread_rwlock_t lock = PTHREAD_RWLOCK_INITIALIZER;
    
    void* do_write_func(void* arg){
        int t;
        while(1){
            usleep(100);
            pthread_rwlock_wrlock(&lock);
            t = count;
            usleep(100);
            printf("%x write: counter=%d,new counter=%d\n",(unsigned int)pthread_self(),t,++count);
            pthread_rwlock_unlock(&lock);
            usleep(100);
        }
        return NULL;
    }
    
    void* do_read_func(void* arg){
        while(1){
            pthread_rwlock_rdlock(&lock);
            printf("%x read: counter=%d\n",(unsigned int)pthread_self(),count);
            pthread_rwlock_unlock(&lock);
            usleep(100);
        }
        return NULL;
    }
    
    int main(){
        pthread_t tid[8];
        int i;
        for(i=0 ; i<3 ; i++){
            pthread_create(&tid[i],NULL,do_write_func,NULL);
        }
        for(i=0 ; i<5 ; i++){
            pthread_create(&tid[i+3],NULL,do_read_func,NULL);
        }
        for(i=0 ; i<8 ; i++){
            pthread_join(tid[i],NULL);
        }
        return 0;
    }
    

    4 条件变量

    //生产者-消费者模型
        //pthread_cond_wait前要先加锁
        //pthread_cond_wait内部会解锁,然后等待条件变量被其它线程激活
        //pthread_cond_wait被激活后会再自动加锁
    #include<stdio.h>
    #include<stdlib.h>
    #include<pthread.h>
    #include<unistd.h>
    
    struct msg{
        int num;
        struct msg* next;
    };
    
    struct msg* head = NULL;
    pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
    pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
    
    void* producer(void* arg){
        struct msg* mp;
        while(1){
            mp = (struct msg*)malloc(sizeof(struct msg));
            mp->num = rand()%1000 + 1;
            printf("produce %d\n",mp->num);
            pthread_mutex_lock(&lock);
            mp->next = head;
            head = mp;
            pthread_mutex_unlock(&lock);
            pthread_cond_signal(&has_product);
            sleep(rand()%5);
        }
    }
    
    void* consumer(void* arg){
        struct msg* mp;
        while(1){
            pthread_mutex_lock(&lock);
            while(head == NULL){
                pthread_cond_wait(&has_product,&lock);
            }
            mp = head;
            head = mp->next;
            pthread_mutex_unlock(&lock);
            printf("consume %d\n",mp->num);
            free(mp);
            sleep(rand()%5);
        }
    }
    
    int main(){
    
        pthread_t ptid,ctid;
        srand(time(NULL));
        pthread_create(&ptid,NULL,producer,NULL);
        pthread_create(&ctid,NULL,consumer,NULL);
    
        pthread_join(ptid,NULL);
        pthread_join(ctid,NULL);
        return 0;
    }
    

    5 信号量

    //通过信号量的方式实现生产者和消费者模型
    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<semaphore.h>
    #include<pthread.h>
    
    #define NUM 5
    sem_t blank_n,product_n; //定义两个信号量
    int queue[NUM]; //资源
    
    void* producer(void* arg){
        int p=0;
        while(1){
            sem_wait(&blank_n); //消耗一个blank_n信号量
            queue[p] = rand()%1000 + 1;
            printf("produce %d\n",queue[p]);
            sem_post(&product_n); //归还一个product_n信号量
            p = (p+1)%NUM;
            sleep(rand()%3);
        }
        return NULL;
    }
    
    void* consumer(void* arg){
        int p=0;
        while(1){
            sem_wait(&product_n); //消耗一个product_n信号量
            printf("consume %d\n",queue[p]);
            sem_post(&blank_n); //归还一个blank_n信号量
            p = (p+1)%NUM;
            sleep(rand()%3);
        }
        return NULL;
    }
    
    int main(){
    
        srand(time(NULL));
        pthread_t ptid,ctid;
        
        sem_init(&blank_n,0,NUM);
        sem_init(&product_n,0,0);
    
        pthread_create(&ptid,NULL,producer,NULL);
        pthread_create(&ctid,NULL,consumer,NULL);
    
        pthread_join(ptid,NULL);
        pthread_join(ctid,NULL);
        
        sem_destroy(&blank_n);
        sem_destroy(&product_n);
        return 0;
    }
    

    6 进程间锁-进程间pthread_mutex

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<fcntl.h>
    #include<sys/mman.h>
    #include<pthread.h>
    #include<string.h>
    #include<sys/wait.h>
    
    struct mt{ //控制的进程间共享资源
        int num;
        pthread_mutexattr_t attr;
        pthread_mutex_t mutex;
    };
    
    int main(){
    
        int fd,i;
        pid_t pid;
        struct mt* mm;
    
        fd = open("my_test",O_RDWR|O_CREAT,0777); //进程间共享数据基于的文件
        ftruncate(fd,sizeof(*mm)); //填入空洞值
        mm = (struct mt*) mmap(NULL,sizeof(*mm),PROT_READ|PROT_WRITE,MAP_SHARED,fd,0); //映射
        close(fd);
        memset(mm,0,sizeof(*mm)); //清零
        pthread_mutexattr_init(&mm->attr); //pthread的属性设置,设置为进程锁
        pthread_mutexattr_setpshared(&mm->attr,PTHREAD_PROCESS_SHARED);
        pthread_mutex_init(&mm->mutex,&mm->attr); //lock为进程锁
    
        pid = fork(); //fork子进程
        if(pid == 0){ //子进程
            for(i=0 ;i<10 ;i++){
                pthread_mutex_lock(&mm->mutex); //上锁访问进程共享资源
                (mm->num)++;
                printf("num++ %d\n",mm->num);
                pthread_mutex_unlock(&mm->mutex);
                sleep(1);
            }
        }else if(pid > 0 ){ //父进程
            for(i=0 ; i<10 ; i++){
                pthread_mutex_lock(&mm->mutex);//上锁访问进程共享资源
                mm->num += 2;
                printf("num+=2 %d\n",mm->num);
                pthread_mutex_unlock(&mm->mutex);
                sleep(1);
            }
            wait(NULL);
        }
    
        pthread_mutex_destroy(&mm->mutex); //销毁锁
        pthread_mutexattr_destroy(&mm->attr); //销毁属性
        munmap(mm,sizeof(*mm)); //解除映射绑定
        unlink("my_test"); //清除临时文件
        
        return 0;
    }
    

    7 进程间锁-文件锁

    #include<stdio.h>
    #include<stdlib.h>
    #include<unistd.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<fcntl.h>
    
    int main(int argc,char* argv[]){
        int fd;
        struct flock f_lock;
        if(argc < 2){
            printf("./a.out filename\n");   
            exit(1);
        }
    
        fd = open(argv[1],O_RDWR);
        f_lock.l_type = F_RDLCK;
        f_lock.l_whence = SEEK_SET;
        f_lock.l_start = 0;
        f_lock.l_len = 0;
    
        fcntl(fd,F_SETLKW,&f_lock);
        printf("get flock\n");
        sleep(5);
        f_lock.l_type = F_UNLCK;
        fcntl(fd,F_SETLKW,&f_lock);
        printf("un flock\n");
        close(fd);
        return 0;
    }
    

    相关文章

      网友评论

          本文标题:Linux 进程

          本文链接:https://www.haomeiwen.com/subject/oechsltx.html