UC成长之路10

回顾:
UC成长之路1
UC成长之路2
UC成长之路3
UC成长之路4
UC成长之路5
UC成长之路6
UC成长之路7
UC成长之路8
UC成长之路9

一、环境变量

  • 每个进程都有自己的环境变量,以bash进程的环境为例来讲解。
  • 如何查看bash的环境变量?使用env命令
  • bash有私有变量(自定义变量)这种变量只能在当前进程使用,当前进程结束,这种变量的周期结束。
  • 环境变量是可以被子进程继承的变量
  • 系统维护着一个全局的变量,这个变量记录着环境变量列表的首地址,extern char **environ;
    eg:编写代码实现遍历进程的环境变量。代码参见tenv.c,编译生成可执行文件tenv
#include <stdio.h>

int main(void)
{
    extern char **environ;
    int i;
    for(i=0;environ[i]!=NULL;i++)
        printf("%s\n", environ[i]);
    return 0;
}

//以上的正真写法应该如下
#include <stdio.h>

int main(int argc, char* argv[], char *envp[])
{
    int i;
    for(i=0;envp[i]!=NULL;i++)
        printf("%s\n", envp[i]);
    return 0;
}

  • 使用exec家族的函数结合环境变量举例
    eg: exec_env.c
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(void)
{
    //創建一個子進程
    pid_t pid = fork();
    if(pid==-1){
        perror("fork");
        return -1; 
    }   
    if(pid==0){//子進程執行的代碼
        //執行tenv可執行程序
        execl("./tenv", "tenv", NULL);
    }   
    else{//父進程執行的代碼
        wait(NULL);//等待子進程結束
    }   
    return 0;
}
//================================
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(void)
{
    char * const p_envp[] = {"key=value", NULL};
    //創建一個子進程
    pid_t pid = fork();
    if(pid==-1){
        perror("fork");
        return -1; 
    }   
    if(pid==0){//子進程執行的代碼
        //執行tenv可執行程序
        execle("./tenv", "tenv", NULL, p_envp);
    }   
    else{//父進程執行的代碼
        wait(NULL);//等待子進程結束
    }   
    return 0;
}

在这里插入图片描述

  • 如何操作当前进程的环境变量?系统提供了一系列的函数:getenv(3), setenv(3), unsetenv(3), putenv(3), clearenv(3)
#include <stdlib.h>
char *getenv(const char *name);
//功能:获取一个环境变量
//参数
//name:指定了要找的环境变量的名字
//返回值:失败返回NULL;成功返回环境变量的值的首地址

eg: 环境变量的操作

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    char *str="caption";
    char* env=getenv(str);
    if(!env){
        printf("not found!!!\n");
        return -1; 
    }   
    printf("%s=%s\n", str, env);
    return 0;
}

在这里插入图片描述

#include <stdlib.h>
int setenv(const char *name, const char *value, int overwrite);
//功能:改变或增加一个环境变量
//参数
//name:指定环境变量的名字
//value:指定了环境变量的值
//overwrite:非0使用value的值更换原来环境变量的值;如果为0原来环境变量的值不改变
//返回值:成功返回0;错误返回-1,并设置errno
int unsetenv(const char *name);
//功能:删除指定的环境变量
//参数
//name:指定要删除环境变量的名字
//返回值:成功返回0;错误返回-1,并设置errno

eg:

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    char *str="caption";
    char* env=getenv(str);
    if(!env){
        printf("not found!!!\n");
        return -1;
    }
    printf("%s=%s\n", str, env);
    //向進程中增加name環境變量
    str="name";
    setenv(str, "tencent", 0);
    env=getenv(str);
    if(!env){
        printf("not found!!!\n");
        return -1;
    }
    printf("%s=%s\n", str, env);
    //改變name環境變量的值,關鍵是overwrite的是0還是非0
    //setenv(str, "google", 0);//1)overwrite=0
    setenv(str, "google", 1);//2)overwrite=1
    env=getenv(str);
    if(!env){
        printf("not found!!!\n");
        return -1;
    }
    printf("after change %s=%s\n", str, env);
    //刪除name環境變量
    unsetenv(str);
    env=getenv(str);
    if(!env){
        printf("not found %s!!!\n", str);
        return -1;
    }
    printf("find %s=%s\n", str, env);


    return 0;
}

在这里插入图片描述

#include <stdlib.h>
int clearenv(void);
//功能:清除环境变量,设置全局变量eviron为NULL
//返回值:成功返回0,失败返回非0

eg:

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

extern char **environ;

int main(void)
{
    char *str="caption";
    char* env=getenv(str);
    if(!env){
        printf("not found!!!\n");
        return -1;
    }
    printf("%s=%s\n", str, env);
    //向進程中增加name環境變量
    str="name";
    setenv(str, "tencent", 0);
    env=getenv(str);
    if(!env){
        printf("not found!!!\n");
        return -1;
    }
    printf("%s=%s\n", str, env);
    //改變name環境變量的值,關鍵是overwrite的是0還是非0
    //setenv(str, "google", 0);//1)overwrite=0
    setenv(str, "google", 1);//2)overwrite=1
    env=getenv(str);
    if(!env){
        printf("not found!!!\n");
        return -1;
    }
    printf("after change %s=%s\n", str, env);
    int c = clearenv();
    if(c){
        printf("clearenv feild...\n");
    }
    if(!environ){
        printf("clear seccuss!\n");
    }

    return 0;
}

在这里插入图片描述

#include <stdlib.h>
int putenv(char *string);//尽量不要使用,容易出错
//功能:增加或改变一个环境变量
//参数
//string:name=value,变量存在则改变其值,不存在则增加
//返回值:成功返回0,失败返回非0

eg:

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

extern char **environ;

int main(void)
{
    char buf[128];
    char *str="name";
    char* env=getenv(str);
    if(!env){
        printf("not found!!!\n");
        return -1;
    }
    printf("%s=%s\n", str, env);
    //1
    strcpy(buf, "name=adair");
    putenv(buf);
    env=getenv(str);
    if(!env){
        printf("not found!!!\n");
        return -1;
    }
    printf("%s=%s\n", str, env);
    //2
    strcpy(buf, "key=value");
    putenv(buf);
    env=getenv("key");
    if(!env){
        printf("not found!!!\n");
        return -1;
    }
    printf("key=%s\n", env);
    env=getenv(str);
    if(!env){
        printf("not found!!!\n");
        return -1;
    }
    printf("%s=%s\n", str, env);

    return 0;
}

在这里插入图片描述

二、管道

  • 管道分为两种:无名管道和有名管道
  • 无名管道:
#include <unistd.h>
int pipe(int pipefd[2]);
//功能:创建管道,
//参数
//int pipedf[2]:有两个整数的数组
//返回值:成功返回0;错误返回-1,errno被设置
//两个文件描述符,通过pipefd数组返回。
//pipefd[0]指向管道的读端,pipefd[1]指向管道的写端,
  • 如何利用这个管道实现进程间通信?
    • 使用pipe创建一个管道,返回两个文件描述符wfd、rfd
    • 父进程创建子进程,子进程复制父进程的PCB,PCB中有文件描述符
    • 子进程负责:向管道中写入内容,关闭读功能,子进程退出;父进程负责:从管道中读取内容,处理读取的数据,回收子进程资源。
  • 要使用无名管道实现进程间的通讯,通讯的进程必须有亲缘关系(父子、兄弟关系)
步骤:
  • 父进程创建管道
  • 父进程创建子进程
  • 父进程的责任
    • 关闭管道写端
    • 从管道中读取数据
    • 对读取的数据处理
    • 回收子进程的资源
  • 子进程的责任
    • 关闭管道读端
    • 向管道写入数据
    • 子进程退出

eg: pipe.c

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(void)
{
    int fd[2];
    char buf[128];
    char msg[]="today is clod!\n";
    //1)創建管道
    int pp = pipe(fd);
    if(pp==-1){
        perror("pipe");
        return -1;
    }
    //2)創建子進程
    pid_t pid = fork();
    if(pid==-1){
        perror("fork");
        return -1;
    }
    if(pid==0){//子進程執行的代碼
        close(fd[0]);//关闭读端
        write(fd[1], msg, strlen(msg));
    }
    else{//父進程執行的代碼
        close(fd[1]);//关闭写端
        int n = read(fd[0], buf, 128);
        write(1, buf, n);
        wait(NULL);
    }

    return 0;
}

在这里插入图片描述

  • 有名管道:实质是一个管道文件,进程间使用管道文件进行通讯。
  • 管道文件只是起到进程间通讯的桥梁作用,这个文件并没有内容,尺寸是0.
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
//功能:参见一个有名管道
//参数:
//pathname:指定了管道文件的名字
//mode:指定了管道文件的权限
//返回值:成功返回0;错误返回-1,errno被设置

eg: fifo.c (fifo先进先出)

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>

int main(int argc, char* argv[])
{
    //創建管道
    int pipe=mkfifo(argv[1], 0664);
    if(pipe==-1){
        perror("mkfifo");
        return -1; 
    }   
    printf("makefifo %s success...\n", argv[1]);
    return 0;
}

在这里插入图片描述

  • 进程需要使用管道文件进行通讯
    eg:程序pA.c打开管道文件hello,向管道文件写入消息;程序pB.c打开管道文件hello,从管道文件中读取消息,将读取到的消息输出到显示器。
    pA.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char* argv[])
{
    char *msg="today is so hot...\n";
    int fd = open(argv[1], O_WRONLY);
    if(fd==-1){
        perror("open");
        return -1; 
    }   
    write(fd, msg, strlen(msg));
    close(fd);
    return 0;
}

在这里插入图片描述

pB.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char* argv[])
{
    char buf[128];
    int fd = open(argv[1], O_RDONLY);
    if(fd==-1){
        perror("open");
        return -1; 
    }   
    int n = read(fd, buf, 128);
    write(1, buf, n); 
    close(fd);
    return 0;
}

在这里插入图片描述
到这里进程结束
练习:编写代码实现bash的功能。编译生成可执行文件 tsh
提示:

#include <stdio.h>

int main(void)
{
    char buf[128];
    char *s; 
    while(1){
        printf("%s$",getcwd(buf, 128));
        s=gets(buf);
        printf("%s\n", s); 
    }   
    return 0;
}

三、信号的基础

  • 信号是软中断,软件模拟的中断机制就是软中断。
  • 使用信号实现进程间的异步通讯。
  • 系统为我们提供了哪些信号?使用命令:kill -l查看
    在这里插入图片描述
  • 信号是发送给进程的信号
  • 信号会经过以下过程:信号的产生、未决状态、阻塞信号、信号的抵达、信号处理(信号捕获)
  • 每个进程都会有对信号的默认处理函数,默认处理:终止当前进程。进程对信号的处理函数可以被子进程继承。SIG_DFL信号的默认处理函数。
  • 程序员如何改变进程对信号的处理函数呢?使用signal(2)函数改变进程对信号的处理函数。
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
//功能:设置信号的处理函数
//参数
//signum:信号编号
//handler:有下面三种情况
//1)SIG_IGN:忽略
//2)SIG_DFL:默认的处理函数
//3)用户自定义的函数地址,用户可以自己编写符合用户的要求
//返回值:错误返回SIG_ERR; 成功返回原来的值

eg: 使用signal(2)设置进程信号的处理函数

#include <stdio.h>
#include <signal.h>

int main(void)
{
    //設置進程忽略2號信號
    signal(2, SIG_IGN);
    while(1);
    return 0;
}

在这里插入图片描述

#include <stdio.h>
#include <signal.h>

int main(void)
{
    //設置進程忽略2號信號
    signal(2, SIG_IGN);
    signal(9, SIG_IGN);
    while(1);
    return 0;
}

在这里插入图片描述

#include <stdio.h>
#include <signal.h>

//信號處理函數
void doit(int n)
{
    printf("receiver...%d\n", n); 
    return;
}

int main(void)
{
    //設置進程忽略2號信號
    signal(2, SIG_IGN);
    signal(2, doit);
    signal(9, SIG_IGN);
    while(1);
    return 0;
}

在这里插入图片描述
补充:Ctrl+c是2信号,Ctrl+\ 是3号信号

eg: 举例说明子进程继承父进程的信号处理函数

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>

//信號處理函數
void doit(int n)
{
    printf("receiver...%d\n", n);
    return;
}

int main(void)
{
    //設置進程忽略2號信號
    signal(2, SIG_IGN);
    signal(3, doit);
    signal(9, SIG_IGN);
    //創建子進程
    pid_t pid = fork();
    if(pid==-1){
        perror("fork");
        return -1;
    }
    if(pid==0){//子進程執行的代碼
        printf("child pid is %d\n", getpid());
        getchar();
        exit(0);
    }
    else{//父進程執行的代碼
        wait(NULL);//父進程等待子進程結束
    }
    return 0;
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值