• 信息安全系统设计基础第十二周学习总结


    信息安全系统设计基础第十二周学习总结

    【学习时间:8 小时】

    【学习内容:process文件夹中的代码理解和执行、故障排除】

    一、代码理解&执行(因为我的虚拟机无法共享文件,所以借用20135211李行之的电脑共同完成代码执行)

    1.argv(文件夹)-argtest.c文件

    #include <stdio.h>
    #include <stdlib.h>
    #include "argv.h"//该函数库中包括freemakeargv.c及makeargv.c函数的调用
    
    int main(int argc, char *argv[]) 
    {
       char delim[] = " 	";//制表符
       int i;
       char **myargv;//见下方解释
       int numtokens;
    
       if (argc != 2)//如果输入的命令字符个数不等于2,就输出标准错误 
       {
            fprintf(stderr, "Usage: %s string
    ", argv[0]);
            return 1;
       }   
      if ((numtokens = makeargv(argv[1], delim, &myargv)) == -1) 
      {
            fprintf(stderr, "Failed to construct an argument array for %s
    ", argv[1]);//翻译过来就是无法构造一个参数数组
            return 1;
       } 
       printf("The argument array contains:
    ");
       for (i = 0; i < numtokens; i++)
            printf("%d:%s
    ", i, myargv[i]);
       execvp(myargv[0], myargv);
    
       return 0;
    }
    

    另附加 freemakeargv.cmakeargv.c函数

    #include <stdlib.h>
    #include "argv.h"
    
    void freemakeargv(char **argv) {
       if (argv == NULL)
             return;
       if (*argv != NULL)
            free(*argv);
       free(argv);
    }
    /*-----------------------------------------------------------------------------*/
    #include <errno.h>
    #include <stdlib.h>
    #include <string.h>
    #include "argv.h"
    
    int makeargv(const char *s, const char *delimiters, char ***argvp)//见下方解释
    {
       int error;
       int i;
       int numtokens;
       const char *snew;
       char *t;
    
       if ((s == NULL) || (delimiters == NULL) || (argvp == NULL)) 
      {
            errno = EINVAL;
            return -1;
      }
       *argvp = NULL;//把字符串数组置为空   
       snew = s + strspn(s, delimiters);//返回字符串s开头连续包含字符串delimiters内的字符数目  
       if ((t = malloc(strlen(snew) + 1)) == NULL) 
            return -1; 
       strcpy(t, snew);   
       numtokens = 0;
       if (strtok(t, delimiters) != NULL)//关于strtok函数的用法见下方
            for (numtokens = 1; strtok(NULL, delimiters) != NULL; numtokens++) ; 
    
       if ((*argvp = malloc((numtokens + 1)*sizeof(char *))) == NULL)
      {//malloc函数请求分配了numtokens+1个字节的空间;如果是返回值是NULL说明分配不成功
            error = errno;
            free(t);//释放malloc函数给指针变量分配的内存空间的函数。使用后该指针变量一定要重新指向NULL。
            errno = error;
            return -1; 
       } 
       if (numtokens == 0) 
            free(t);
       else 
      {
            strcpy(t, snew);
            **argvp = strtok(t, delimiters);
            for (i = 1; i < numtokens; i++)
                *((*argvp) + i) = strtok(NULL, delimiters);
      } 
    *((*argvp) + numtokens) = NULL;   
    return numtokens;
    } 
    

    关于该系列代码的难点理解 

    【1.为什么是* *myargv?】 

    经过查阅得知,比较准确的说法是: **相相当于二级指针,char **就是指向字符型指针的指针。最常使用的地方就是 int main(int argc,char **argv),相当于int main(int argc,char *argv[])。也就是说,可以看作是指向了字符串数组

    【2.为什么是 int makeargv(const char *s, const char *delimiters, char * **argvp)】

    把最后一个参数理解为向字符串数组取地址(从左到右,第一个代表取地址,后两个 **代表上文中说过的字符串数组)

    【3.关于strtok函数?】 

    strtok函数用来将字符串分割成一个个片段,它的原型是char *strtok(charr s[],const char *delim)。只要在s中遇到delim中包含的字符(不一定是delim),就把这个字符改成。每次调用成功后返回的都是被分割出的片段的指针。

    【4.errno与error?】

    前者是记录系统最后一次错误的函数;后者是系统错误

    2.env文件夹-environ.c文件

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void)
    {
        printf("PATH=%s
    ", getenv("PATH"));//getenv函数用来取得参数PATH环境变量的值,执行成功则返回该内容的指针
        setenv("PATH", "hello", 1);//见下方解释
        printf("PATH=%s
    ", getenv("PATH"));
    #if 0
        printf("PATH=%s
    ", getenv("PATH"));
        setenv("PATH", "hellohello", 0);
        printf("PATH=%s
    ", getenv("PATH"));
    
    
        printf("MY_VER=%s
    ", getenv("MY_VER"));//版本
        setenv("MY_VER", "1.1", 0);
        printf("MY_VER=%s
    ", getenv("MY_VER"));
    #endif
        return 0;
    }
    

    【setenv函数的作用?】 

    setenv用来在本次函数运行的过程中增加或者修改环境变量。当最后一个参数不为0的时候,原来的内容会被修改为第二个参数所指的内容。

    3.env文件夹-environvar.c文件

    #include <stdio.h>
    int main(void)
    {
        extern char **environ;
        int i;
        for(i = 0; environ[i] != NULL; i++)
            printf("%s
    ", environ[i]);
    
        return 0;
    }
    

    【environ变量是什么?】

    该变量指向一个叫“environment”的字符串数组。包括USER(登录用户的名字),LOGNAME(与user类似),HOME(用户登录目录),LANG(地域名),PATH等

    4.pipe文件夹-consumer.c文件

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <fcntl.h>
    #include <limits.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    
    #define FIFO_NAME "/tmp/myfifo"
    #define BUFFER_SIZE PIPE_BUF
    int main()
    {
    int pipe_fd;
    int res;
    
    int open_mode = O_RDONLY;
    char buffer[BUFFER_SIZE + 1];
    int bytes = 0;
    
    memset(buffer, 0, sizeof(buffer));
    
    printf("Process %d opeining FIFO O_RDONLY 
    ", getpid());
    pipe_fd = open(FIFO_NAME, open_mode);
    printf("Process %d result %d
    ", getpid(), pipe_fd);
    
    if (pipe_fd != -1) {
        do {
            res = read(pipe_fd, buffer, BUFFER_SIZE);
            bytes += res;
        } while (res > 0);
        close(pipe_fd);
    } else {
        exit(EXIT_FAILURE);
    }
    
    printf("Process %d finished, %d bytes read
    ", getpid(), bytes);
    exit(EXIT_SUCCESS);
    }
    

    【1.PIPE_BUF的值是多少?】

    4096字节

    【2.memset函数用法?】

    原型:memset(void *s,int ch,size_t n);将s中前n个字节用ch替换并返回s

    【3.open函数用法?】 

    open(const char *pathname,int flags);第一个参数是欲打开的文件路径字符串,第二个参数是打开方式

    【4.FIFONAME是什么?】 

    这里需要补充一下fifo的含义,它是一种文件类型,可以通过查看文件stat结构中的stmode成员的值来判断文件是否是FIFO文件。fifo是用来在进程中使用文件来传输数据的,也具有管道特性,可以在数据读出的时候清除数据。

    5.env文件夹-producer文件

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <fcntl.h>
    #include <limits.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    
    #define FIFO_NAME "/tmp/myfifo"
    #define BUFFER_SIZE PIPE_BUF
    #define TEN_MEG (1024 * 1024 * 10)
    
    int main()
    {
    int pipe_fd;
    int res;
    int open_mode = O_WRONLY;
    
    int bytes = 0;
    char buffer[BUFFER_SIZE + 1];
    
    if (access(FIFO_NAME, F_OK) == -1) {//检查文件是否有相应的权限
        res = mkfifo(FIFO_NAME, 0777);//依据FIFO_NAME创建fifo文件,0777依次是相应权限
        if (res != 0) {
            fprintf(stderr, "Could not create fifo %s 
    ",
                FIFO_NAME);
            exit(EXIT_FAILURE);
        }
    }
    
    printf("Process %d opening FIFO O_WRONLY
    ", getpid());
    pipe_fd = open(FIFO_NAME, open_mode);
    printf("Process %d result %d
    ", getpid(), pipe_fd);
    
    if (pipe_fd != -1) {
        while (bytes < TEN_MEG) {
            res = write(pipe_fd, buffer, BUFFER_SIZE);
            if (res == -1) {
                fprintf(stderr, "Write error on pipe
    ");
                exit(EXIT_FAILURE);
            }
            bytes += res;
        }
        close(pipe_fd);
    } else {
        exit(EXIT_FAILURE);
    }
    
    printf("Process %d finish
    ", getpid());
    exit(EXIT_SUCCESS);
    

    }

    6.env文件夹-testmf.c文件

    #include  <stdio.h>
    #include  <stdlib.h>
    #include  <sys/types.h>
    #include  <sys/stat.h>
    
    int main()//就是创建fifo文件
    {
        int res = mkfifo("/tmp/myfifo", 0777);
        if (res == 0) {
            printf("FIFO created 
    ");
        }
        exit(EXIT_SUCCESS);
    }

    (修改testmf.c代码)

    (修改之后的执行结果)

    7.pipe文件夹-pipe.c文件

    #include    <stdio.h>
    #include<stdlib.h>
    #include    <unistd.h>
    
    #define oops(m,x)   //当linux系统执行代码遇到问题时,就会报告oops
    { perror(m); exit(x); }
    
    int main(int ac, char **av)
    {
        int thepipe[2], newfd,pid;              
    
        if ( ac != 3 ){//输入的命令长度不等于3
            fprintf(stderr, "usage: pipe cmd1 cmd2
    ");
            exit(1);
        }
        if ( pipe( thepipe ) == -1 )    //以下是各种错误   
            oops("Cannot get a pipe", 1);
    
        if ( (pid = fork()) == -1 )         
            oops("Cannot fork", 2);
    
        if ( pid > 0 ){         
            close(thepipe[1]);  
    
            if ( dup2(thepipe[0], 0) == -1 )
                oops("could not redirect stdin",3);
    
            close(thepipe[0]);  
            execlp( av[2], av[2], NULL);
            oops(av[2], 4);
        }
    
        close(thepipe[0]);      
    
        if ( dup2(thepipe[1], 1) == -1 )
            oops("could not redirect stdout", 4);
    
        close(thepipe[1]);      
        execlp( av[1], av[1], NULL);
        oops(av[1], 5);
    }

    8.pipe文件夹-stdinredir1.c文件

    #include    <stdio.h>
    #include    <fcntl.h>
    
    int main()
    {
        int fd ;
        char    line[100];
    
        fgets( line, 100, stdin ); printf("%s", line );//就是打印输入的字符串
        fgets( line, 100, stdin ); printf("%s", line );
        fgets( line, 100, stdin ); printf("%s", line );
    
        close(0);
        fd = open("/etc/passwd", O_RDONLY);
        if ( fd != 0 ){//打开或者创建失败的时候才会执行
            fprintf(stderr,"Could not open data as fd 0
    ");
            exit(1);
        }
    
        fgets( line, 100, stdin ); printf("%s", line );
        fgets( line, 100, stdin ); printf("%s", line );
        fgets( line, 100, stdin ); printf("%s", line );
    }

    9.pipe文件夹-testtty.c文件

    #include <unistd.h>
    int main()
    {
        char *buf = "abcde
    ";
        write(0, buf, 6);
    }
    

    【write函数】 

    write(int handle,void *buf,int nbyte); 第一个参数是文件描述符,第二个参数是指向一端内存单元的指针,第三个参数是要写入指定文件的字节个数;成功时返回字节个数,否则返回-1。

    10.signal文件夹-sigactdemo1.c文件

    #include    <stdio.h>
    #include<unistd.h>
    #include    <signal.h>
    #define INPUTLEN    100
    void inthandler();  
    int main()
    {
        struct sigaction newhandler;//见下方解释 
        sigset_t blocked;   //信号集,用来描述信号的集合,与信号阻塞相关函数配合使用
        char x[INPUTLEN];
        newhandler.sa_handler = inthandler; //函数指针
        newhandler.sa_flags = SA_RESTART|SA_NODEFER
            |SA_RESETHAND;  //sa_flags是一个位掩码。这里,第一个参数使得被信号打断的一些原语“正常返回”
        sigemptyset(&blocked);  
        sigaddset(&blocked, SIGQUIT);   
        newhandler.sa_mask = blocked;   
        if (sigaction(SIGINT, &newhandler, NULL) == -1)
            perror("sigaction");
        else
            while (1) {
                fgets(x, INPUTLEN, stdin);
                printf("input: %s", x);
            }
        return 0;
    }
    void inthandler(int s)
    {
        printf("Called with signal %d
    ", s);
        sleep(s * 4);
        printf("done handling signal %d
    ", s);
    }
    

    具体参见http://www.cnblogs.com/gogly/articles/2416989.html 

    【sigaction结构体,用来查询或设置信号处理方式。比如它指定了对特定信号的处理,信号所传递的信息,信号处理函数执行过程中应该屏蔽掉哪些函数等。】

    11.signal文件夹-sigdemo1.c文件夹

    #include    <stdio.h>
    #include    <signal.h>
    void    f(int);         
    int main()
    {
        int i;
        signal( SIGINT, f );        
        for(i=0; i<5; i++ ){        
            printf("hello
    ");
            sleep(2);
        }
    
        return 0;
    }
    
    void f(int signum)          
    {
        printf("OUCH!
    ");
    }
    

    这样的代码是无法在输出的时候体现出对f的调用的。我认为其中signal( SIGINT, f );这一句中,因为f的返回值是“void”,所以无法体现。

    补充:signal函数,原型 signal(参数1,参数2);,其中参数1是我们进行处理的信号,参数2是我们处理的方式。

    后来我对代码进行了修改,体现了对f函数的调用

    12.signal文件夹-sigdemo3.c文件

    #include    <stdio.h>
    #include<string.h>
    #include    <signal.h>
    #include<unistd.h>
    
    #define INPUTLEN    100
    
    int main(int argc, char *argv[])
    {
        void inthandler(int);
        void quithandler(int);
        char input[INPUTLEN];
        int nchars;
    
        signal(SIGINT, inthandler);//^C 
        signal(SIGQUIT, quithandler);//^
    
        do {//输入什么,就输出什么(在read函数不发生错误的情况下)
            printf("
    Type a message
    ");
            nchars = read(0, input, (INPUTLEN - 1));
            if (nchars == -1)
                perror("read returned an error");
            else {
                input[nchars] = '';
                printf("You typed: %s", input);
            }
        }
        while (strncmp(input, "quit", 4) != 0);//只有输入quit的时候才会退出
        return 0;
    }
    
    void inthandler(int s)
    {
        printf(" Received signal %d .. waiting
    ", s);
        sleep(2);
        printf("  Leaving inthandler 
    ");
    }
    
    void quithandler(int s)
    {
        printf(" Received signal %d .. waiting
    ", s);
        sleep(3);
        printf("  Leaving quithandler 
    ");
    }

    13.exec3.c文件

    #include <stdio.h>
    #include <unistd.h>
    
    int main()
    {
        char    *arglist[3];
        char*myenv[3];
        myenv[0] = "PATH=:/bin:";
        myenv[1] = NULL;
    
        arglist[0] = "ls";
        arglist[1] = "-l";
        arglist[2] = 0 ;
        printf("* * * About to exec ls -l
    ");
    //  execv( "/bin/ls" , arglist );
    //  execvp( "ls" , arglist );
    //  execvpe("ls" , arglist, myenv);
    
        execlp("ls", "ls", "-l", NULL);
        printf("* * * ls is done. bye
    ");
    }
    

    【execlp函数?】 

    从PATH环境变量中查找文件并执行。原型:int execlp(const char *file,const char *arg,……); 从PATH环境变量所指的目录中查找符号参数file的文件名,然后将第二个及以后的参数当作该文件的argv[0],argv[1],……,最后一个参数必须用NULL结束。

    【execv函数?】 

    原型:int execv(const char *pathname,char *const argv[]);装入并运行其他程序 对比:execvp函数原型: int execvp(const char *file,char *const argv[]);

    二、关于执行时的困难&解决

    1.a.out如何运行?

    在Linux系统中,如果没有给编译的文件指定文件名,就会自动命名为*.out文件。执行该类文件的时候,直接是 绝对路径/a.out。  经过尝试,发现给出的a.out文件“格式错误”,无法运行。  后经依次尝试,发现给出的所有可执行文件都有类似的问题。这个问题的原因在下面的操作中逐渐得到了解答。

    (选择打开方式为“在终端执行”)

    2.如何在linux中执行.sh文件?

    (参考http://zhidao.baidu.com/question/1733278229438599307.html

    首先你要让文件有能够执行的权限,比如你的文件是a.sh那么你可以 chmod +x a.sh 然后运行文件就可以了 ./a.sh 这样运行是a.sh在当前工作目录,如果文件没在当前目录,那么就需要用绝对路径来执行,比如 /opt/a.sh /opt/test/a.sh

    3.关于指针数组与数组指针的区别?

    (参考http://www.cnblogs.com/mq0036/p/3382732.html

    • 数组指针(也称行指针)

      • 定义 int (*p)[n];()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
      • 如要将二维数组赋给一指针,应这样赋值:

        int a[3][4];    
        int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。
        p=a;//将该二维数组的首地址赋给p,也就是a[0]或&a[0][0]
        p++;   //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
        
      • 数组指针也称指向一维数组的指针,亦称行指针。

    • 指针数组

      • 定义 int p[n];[]优先级高,先与p结合成为一个数组,再由int说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1时,则p指向下一个数组元素,这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 p=a; 这里p表示指针数组第一个元素的值,a的首地址的值。
      • 如要将二维数组赋给一指针数组:

        int *p[3];
        int a[3][4];
        p++; //该语句表示p数组指向下一个数组元素。注:此数组每一个元素都是一个指针
        for(i=0;i<3;i++)
            p[i]=a[i];
        

        这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2] 所以要分别赋值。

    4.关于函数指针和指针函数的区别?

    参考(http://blog.csdn.net/htyurencaotang/article/details/11490081

    • 指针函数是指带指针的函数,即本质是一个函数。函数返回类型是某一类型的指针

      • 类型标识符 *函数名(参数表) ;具体格式:int *f(x,y);

      • 首先它是一个函数,只不过这个函数的返回值是一个地址值。指针函数一定有函数返回值,而且在主调函数中,函数返回值必须赋给同类型的指针变量。例如:

        01.float *fun();  
        02.float *p;  
        03.p = fun(a);  
        
    • 函数指针是指向函数的指针变量,即本质是一个指针变量。 

      • 指向函数的指针包含了函数的地址,可以通过它来调用函数。声明格式如下: 类型说明符 (*函数名)(参数)
      • 使用的时候:

        01.int (*f)(int x); /*声明一个函数指针 */  
        02.f=func; /*将func函数的首地址赋给指针f */  
        

    5.execl1执行故障?

    在直接 gcc execl1 -o execl1.2(因为之前已经有execl可执行文件了)之后,系统提示如图。后来改变方法,先生成.o 文件再生成可执行文件,就可以执行代码了。

    通过网上搜索,发现出现这个错误提示是因为使用了第三方库而没有把它的.m文件添加到compile source中去,而是可能只把这些文件copy到当前文件夹里就觉得可以了;这样在直接编译链接执行的时候根本没有自动引用(网上提供的解决方法建立在专门的编写软件基础上,在target--build phases--compile source中添加.m;不过针对我们的情况,可以分步完成以保证最终生成可执行文件)。

    6.consumer执行故障?

    开始的时候直接gcc consumer.o -o consumer仍然出现上文中的错误。后来删除consumer .o 文件之后分步编译,可以执行。

    7.producer.c函数运行错误?

    开始的时候,提示有错误。如图。  后来查询了access函数,发现它的功能是确定文件或者文件夹的访问权限。如果指定的存取方式有效,则返回0;否则返回-1.其中,f_ok只是判断该文件是否存在。而整个函数需要头文件<unistd.h>。加上该头文件之后,代码可以正常编译。

    三、其他代码执行结果

    (fifo文件夹-c,p,tags代码执行)

    (forkdemo1.c执行)

    (forkdemo2.c文件)

    (forkdemo3.c执行)

    (forkdemo4.c执行)

    (forkgdb.c执行)

    (listargs.c代码执行)

    (pipe.c代码执行)

    (pipedemoo2.c代码执行。其中,其主体结构为do……while(1),是一个无法停止的循环。可以用ctrl+C强制退出代码执行)

    (sigdemo2.c代码执行,同上)

    (stdindir2.c代码执行)

    (testbuf1.c代码执行)

    (testpid.c代码执行)

    (testpp.c代码执行;并没有结果输出,知识申请了一个20字节大小的空间)

    (waitdemo1.c代码执行)

    (waitdemo2.c代码执行)

    (watch.sh程序,每sleep30秒打印一次)

    (watch2.sh程序,结果同上)

    四、疑问

    1.关于argtest.c代码

    snew = s + strspn(s, delimiters);//返回字符串s开头连续包含字符串delimiters内的字符数目  
    if ((t = malloc(strlen(snew) + 1)) == NULL) 
        return -1; 
    

    strspn函数返回的是字符个数,怎么再加上字符串s ?

    2.res = mkfifo(FIFO_NAME, 0777);我知道0777是对应的权限;然而这四个数字分别对应哪些用户的权限?

    补充:每个文件的三组权限(拥有者,所属用户组,其他用户,记住这个顺序是一定的)

    3.sigactdemo1.c代码:

    newhandler.sa_handler = inthandler; 这样赋值对吗?(原型是void inthandler(int s);)

    五、心得

    这次学习过程与之前有着很大的不同。就像我在期中总结的时候说的那样,我在实践方面需要进一步加强;这次如此多的代码的执行正好很好地锻炼了我的动手能力。比起理解代码,动手让代码“跑出来”更需要自己动脑筋(百科里会有对某个函数的详细解释,但很少有对某段代码如何运行的教程)。

  • 相关阅读:
    Sql批处理语句
    使用waitfor 语句
    将文件分对话拆分
    集合
    用户登录系统
    fromkeys() keys() values() items()
    通讯录程序
    字符串分割方法split()函数
    装逼的本质就是把同一东西说成不同的事物
    字典
  • 原文地址:https://www.cnblogs.com/lwr-/p/5004420.html
Copyright © 2020-2023  润新知