精华内容
下载资源
问答
  • read函数的底层调用

    千次阅读 2018-03-14 19:56:49
    很久之前对于虚拟文件部分的一些内容进行了整理。对于read的底层过程只是描述了一个大概,这次主要是对于每一部分的东向西进行详细的描述。...具体的我就从open系统调用开始并且详细描述read的过程。open系统...

    很久之前对于虚拟文件部分的一些内容进行了整理。对于read的底层过程只是描述了一个大概,这次主要是对于每一部分的东向西进行详细的描述。go go go(方便对于面试时候的整理)。

    参考 https://www.cnblogs.com/wangzahngjun/p/5553793.html

    虚拟文件部分的整理参考原来博客的一片文章。具体的我就从open系统调用开始并且详细描述read的过程。

    open系统调用:


    对于sys_read 函数的调用过程如下

    文件:
    1.read()到sys_read();
    2.通过task_struct->file_struct->fd[]->file*->f_op->read();
    3通过目录项,找到该文件的inode;
    4.在文件表中,通过文件内容偏移量计算出要读取的页;
    5.通过inode找到文件对应的address_space;
    6.在address_space中访问该文件的页缓存树,查找对应的页缓存结点:
    (1)如果页缓存命中,那么直接返回文件内容;
    (2)如果页缓存缺失,那么产生一个页缺失异常,创建一个页缓存页,同时通过inode找到文件该页的磁盘地址,读取

    相应的页填充该缓存页;重新进行第6步查找页缓存;

    大体过程如上面所述。

    具体而言的,实际的一个文件系统也是如上面所说的过程,以ext2 问价系统来描述。


    对于ext2而言过程如下 sys_read->file->fop->read->do_sync_read->filp->f_op->aio_read->generic_file_aio_read.

    do_sync_read 函数


    ssize_t do_sync_read(struct file *filp, char __user *buf, size_t len, loff_t *ppos)
    {
        struct iovec iov = { .iov_base = buf, .iov_len = len };//获取用户读字符保存buff空间的地址和所传进来的长度
        struct kiocb kiocb;//用来跟踪当前同步和异步的io
        ssize_t ret;
    
        init_sync_kiocb(&kiocb, filp);
        kiocb.ki_pos = *ppos;
        kiocb.ki_left = len;
    
        for (;;) {
            ret = filp->f_op->aio_read(&kiocb, &iov, 1, kiocb.ki_pos); //读操作开始 
            if (ret != -EIOCBRETRY)
                break;
            wait_on_retry_sync_kiocb(&kiocb);
        }
    
        if (-EIOCBQUEUED == ret)
            ret = wait_on_sync_kiocb(&kiocb);
        *ppos = kiocb.ki_pos;
        return ret;

    首先所有的参数被一层一层的传递下来到达文件系统提供的read函数。

    调用的是filp->op->aio_read 同步调用这个立即返回读到的的数据。

    他的底层调用的是generic_file_aio_read函数功能分析

    if (filp->f_flags & O_DIRECT) {//设置直接读取不从页缓存中读。
            loff_t size;
            struct address_space *mapping;
            struct inode *inode;
    
            mapping = filp->f_mapping;
            inode = mapping->host;
            if (!count)
                goto out; /* skip atime */
            size = i_size_read(inode);
            if (pos < size) {
                retval = filemap_write_and_wait_range(mapping, pos,
                        pos + iov_length(iov, nr_segs) - 1);
                if (!retval) {
                    retval = mapping->a_ops->direct_IO(READ, iocb,
                                iov, pos, nr_segs);
                }
                if (retval > 0)
                    *ppos = pos + retval;
                if (retval) {
                    file_accessed(filp);
                    goto out;
                }
            }

    这个参数是open函数的一个参数具体查看man 2 open 对于这个标志进行介绍具体可以总结未以下几点

    O_DIRECT,绕过缓冲区高速缓存,直接IO
    直接IO:Linux允许应用程序在执行磁盘IO时绕过缓冲区高速缓存,从用户空间直接将数据传递到文件或磁盘设备,称为直接IO(direct IO)或者裸IO(raw IO)。
    应用场景:数据库系统,其高速缓存和IO优化机制均自成一体,无需内核消耗CPU时间和内存去完成相同的任务。
    使用直接IO的弊端:可能会大大降低性能,内核对缓冲区告诉缓存做了不少优化,包括:按顺序预读取,在成簇磁盘块上执行IO,允许访问同一文件的多个进程共享高速缓存的缓冲区。
    使用方法:在调用open函数打开文件或设备时指定O_DIRECT标志。
    注意可能发生的不一致性:若一进程以O_DIRECT标志打开某文件,而另一进程以普通(即使用了高速缓存缓冲区)打开同一文件,则由直接IO所读写的数据与缓冲区高速缓存中内容之间不存在一致性,应尽量避免这一场景。
    使用直接IO需要遵守的一些限制:
    用于传递数据的缓冲区,其内存边界必须对齐为块大小的整数倍
    数据传输的开始点,即文件和设备的偏移量,必须是块大小的整数倍
    待传递数据的长度必须是块大小的整数倍。

    不遵守上述任一限制均将导致EINVAL错误。

    如果没有设置上面标志那么它会从页缓存中读取

    for (seg = 0; seg < nr_segs; seg++) {
            read_descriptor_t desc;
    //将iovec结构转换成read_descriptor_t的结构
            desc.written = 0;
            desc.arg.buf = iov[seg].iov_base;//上面的参数重新打包一下啊
            desc.count = iov[seg].iov_len;
            if (desc.count == 0)
                continue;
            desc.error = 0;
            do_generic_file_read(filp, ppos, &desc, file_read_actor);//调用当前的函数进行读取。
            retval += desc.written;
            if (desc.error) {
                retval = retval ?: desc.error;
                break;
            }
            if (desc.count > 0)
                break;
        }
    out:
        return retval;

    do_generic_file_read()函数的主要功能是 //查找对应文件的页缓存 address_space 是根据文件的index所查找的对应的缓存页好。 find_get_page()使用地址空间的基树查找索引为index的页。尝试去找到第一个被请求的页。如果这个页不在页缓存中,就跳转到标号no_cached_page处,如果该页不是最新的,就跳转到标号page_not_up_to_date_locked处,如果在尝试去获取这个页面的独占权,即加锁的时候,没有获取成功,则跳转到page_not_up_to_date处。;

    atic void do_generic_file_read(struct file *filp, loff_t *ppos,
            read_descriptor_t *desc, read_actor_t actor)
    {
        struct address_space *mapping = filp->f_mapping;  //获取页高速缓存
        struct inode *inode = mapping->host;//获取inode
        struct file_ra_state *ra = &filp->f_ra;
        pgoff_t index;
        pgoff_t last_index;
        pgoff_t prev_index;
        unsigned long offset;      /* offset into pagecache page */
        unsigned int prev_offset;
        int error
        . ............................
        .............................

    如果查到了但可能加锁的过程中出现的一下问题。

    page_not_up_to_date_locked:
            /* Did it get truncated before we got the lock? */
      //有可能在锁页面的时候`有其它的进程将页面移除了页缓存区
             //在这种情况下:将page解锁`并减少它的使用计数,重新循环```
             //重新进入循环后,在页缓存区找不到对应的page.就会重新分配一个新的page
            if (!page->mapping) {
                unlock_page(page);
                page_cache_release(page);
                continue;
            }
    
            /* Did somebody else fill it already? */
             //在加锁的时候,有其它的进程完成了从文件系统到具体页面的映射?
             //在这种情况下,返回到page_ok.直接将页面上的内容copy到用户空间即可
            if (PageUptodate(page)) {
                unlock_page(page);
                goto page_ok;
            }

    如果不是最新的页面则需要更新。更新的时候可能的问题。在这里调用实际的读page的操作mpping->a_ops->readpage()对该页进行读取。如果成功读取了一个页,检查其是否是最新的,如果是最新的,则跳转到标号page_ok处。如果发生了同步读错误,就设置其error,并跳转到readpage_error处。

    readpage:
            /* Start the actual read. The read will unlock the page. */
            error = mapping->a_ops->readpage(filp, page);  //调用具体的readpage函数,在后面会分析。
    
            if (unlikely(error)) {   //如果发生了AOP_TRUNCATED_PAGE错误,则回到find_page重新进行获取
                if (error == AOP_TRUNCATED_PAGE) {
                    page_cache_release(page);
                    goto find_page;
                }
                goto readpage_error;
            }
    //如果PG_uptodata标志仍然末设置.就一直等待,一直到page不处于锁定状态
             //  在将文件系统的内容读入page之前,page一直是处理Lock状态的。一直到
             //读取完成后,才会将页面解锁
            if (!PageUptodate(page)) {
                error = lock_page_killable(page);
                if (unlikely(error))
                    goto readpage_error;
                if (!PageUptodate(page)) {
                    if (page->mapping == NULL) {
                        /*
                         * invalidate_inode_pages got it
                         */
                        unlock_page(page);
                        page_cache_release(page);
                        goto find_page;
                    }
                    unlock_page(page);
                    shrink_readahead_size_eio(filp, ra);
                    error = -EIO;
                    goto readpage_error;
                }
                unlock_page(page);
            }
    
            goto page_ok;  //读取成功

    当该页不存在的时候:

    这里主要讲述了当没有改页时时如何处理的。最后我们来看下do_generic_file_read函数的out
    
    out:
        ra->prev_pos = prev_index;
        ra->prev_pos <<= PAGE_CACHE_SHIFT;
        ra->prev_pos |= prev_offset;
    
        *ppos = ((loff_t)index << PAGE_CACHE_SHIFT) + offset;  //计算实际的偏移量
        file_accessed(filp);//更新文件的最后一次访问时间。
    }
    具体上来说就这莫多东西。


    展开全文
  • 浅谈C语言函数调用与系统调用

    千次阅读 2019-11-12 09:55:50
    很多初学C语言的同学,亦或者开发很少接触系统底层的同学可能会认为函数调用以及系统调用是一回事。因为在应用程序,两者都被抽象成接口去给应用程序调用。其实函数调用和系统调用还是有区别,我们通过下图先有个...

    1. 函数调用和系统调用概述

    1.1 定性的去区分函数调用和系统调用

    很多初学C语言的同学,亦或者开发中很少接触系统底层的同学可能会认为函数调用以及系统调用是一回事。因为在应用程序,两者都被抽象成接口去给应用程序调用。其实函数调用和系统调用还是有区别,我们通过下图先有个全局的了解!

    图1-1 系统功能模块关联图

     

    从图1-1 我们可以知道应用程序访问内核,主要通过两种方式:中断和系统调用接口。

    其一,中断是置于程序流程之外的,所以这种方式并不是我们访问内核的普遍方式,更多的是因为程序异常而引起的中断;

    其二,就是系统调用接口,例如使用 open、read 以及 write 去操作文件数据(相当于去访问硬盘)。

    而函数调用是无法直接访问内核的,函数调用需要访问内核,也只能通过系统调用的方式。

    所以,我们可以通过这种方式定性地去区分函数调用和系统调用: 接口直接访问内核的属于系统调用,其他则为函数调用

    1.2 为什么要封装好系统调用

    我们知道,32位 linux系统为了保证程序独立性,给每一个进程分配独立的4G地址空间(虚拟内存空间),但是4G地址空间又被划分为两个空间:用户空间以及内核空间,如下图:

    图1-2 进程地址空间划分图

    用户空间,存储的是一些进程常见的数据,例如代码、变量数据、堆区和栈区等,需要注意的是内核空间,虽然说系统给每一个进程都分配1G大小的内核空间,但是实际上,内核空间是操作系统中进程公用的。公用就会带来一个现实性的问题,就比如我们可以在自己的房间干任何事情(用户空间),去不能在公共场合(内核空间)为所欲为,活动在公共场合(内核空间),我们的行为会受到限制,对于操作系统也一样道理,内核空间属于进程公用,进程去访问内核必须保证不会给其他进程造成影响。

    如何去保证进程操作系统中公用的内核空间不会出现非法操作,打个比方说:如何保证银行内部数据不被非法操作,银行是提供一个ATM给你,你只能选择上面的接口操作。操作系统也“学习”了这样的模式,封装接口,保证数据操作正确性!

    不仅仅应用程序无法直接访问内核空间,只能通过系统调用访问内核空间,而且内核对用户空间的数据的不信任,对于通过系统调用的传递过来的数据也会做相应的检查,确保内核安全,确保内核安全是必须的,内核属于系统的软件核心,内核崩溃会导致整个软件系统的崩溃。为了保证应用程序传递的数据合法性以及内核数据的绝密性,通常会使用copy_from_user去检验和拷贝传递过来的数据,使用copy_to_user()检验和拷贝数据给应用程序。

    2.函数调用

    2.1 什么叫做函数调用

    int add(int a, int b){
    
        return(a + b);
    }
    
    int main(){
    
        int sum;
        
        sum = add(1 + 2);
        
        return 0;
    }

    如上是一个最简单的函数调用过程,了解什么叫函数调用,我们可以通过了解什么是函数?

    函数(function)是完成特定任务的独立程序代码单元。

                                                                                                                                                         ----《C Primer Plus》

    再来了解为啥需要使用函数?

    首先,使用函数可以省去编写重复代码的苦差。如果程序多次完成某一项。如果程序需要多次完成某项任务,那么只需编写一个适合的函数,就可以在需要时使用这个函数,或者在不同的程序中使用该函数,就像许多程序使用putchar() 一样。

    其次,即使程序只完成某项任务一次,也值得使用函数。因为函数使程序更加模块化,从而提高了程序代码的可读性,更方便后期修改、完善。

                                                                                                                                                         ----《C Primer Plus》

    函数调用是很好理解的,提高代码重用性以及模块性,调用相对应函数,得到某种结果。

    2.1 函数调用的过程

    函数调用会发生函数压栈以及出栈过程,具体可以参考如下博文和视频(博主对汇编不熟悉,所以就不具体分析了,以免误人子弟!)。

    浅析函数的调用过程

    汇编中的函数调用中栈的工作过程

    3.系统调用

    3.1 什么叫做系统调用

    为了和用户空间上运行的进程进行交互,内核提供了一组接口。透过该接口,应用程序可以访问硬件设备和其他操作系统资源。这组接口在应用程序和内核之间扮演了使者的角色,应用程序发送各种请求,而内核负责满足这些请求(或者让应用程序暂时搁置)

                                                                                                                                             ----《Linux内核设计与实现》

    3.2 系统调用的过程

    系统调用和函数调用在应用程使用几乎没有差别,但是内部实现过程是完全不同的,函数调用是通过函数入口地址直接跳转函数服务程序,而系统调用并非直接跳转系统调用服务程序,因为内核地址对于应用程序是不可用的,需要通过中断的方式进入内核态,再通过一系列的操作找到对应的服务程序。

    我们可以通过下图有一个了解:

    图3-1 系统调用过程流程图

    希望更详细的了解系统调用过程可以看看下面这篇博文:

    ioctl系统调用过程(深入Linux(ARM)内核源码)

    展开全文
  • Shell函数调用

    万次阅读 2016-09-03 23:31:46
     ----------函数介绍  ----------函数定义  ----------函数使用  ----------区分return和exit  ----------删除函数  ----------注意事项   1、介绍函数 

    本章学习内容

            ----------函数介绍

            ----------函数定义

            ----------函数使用

            ----------区分returnexit

            ----------删除函数

            ----------注意事项



        

    1、介绍函数 

        通俗地讲,函数就是将一组功能相对独立的代码集中起来,形成一个代码块,这个代码可以完成某个具体的功能(这里理解为命令的堆积也可以)。从本质上讲,函数是一个函数名到某个代码块的映射。也就是说,用户在定义了函数之后,就可以通过函数名来调用其所对应的一组代码(称为函数调用)。




    2、定义函数

        <1>俩种方式     

           ▲function fname {

               command
               command
             }

           ▲fname {

               command
               command
             } 

        <2>函数名不能和命令相同,命令优先级高于函数

        <3>函数命名规则

           不可与命令相同,否则命令无法正常使用。  

           函数优先级>命令

           不可定义与函数名同名的别名,否则函数无法使用。

           别名优先级>函数

           所以优先级:别名 >  函数   >  命令

        <4>函数中的变量设为局部变量,防止与shell的变量冲突

           验证

    # 创建函数文件fun2,定义test函数
    [root@localhost ~]#vim fun2
    function test()
    {
     a=first
     echo "a=$a"
    }
    # 编写脚本testfun2.sh
    [root@localhost ~]#vim testfun2.sh 
    #!/bin/bash
    #
    a=second
    source fun2
    test
    echo "a=$a"
    # 执行脚本
    [root@localhost ~]#bash testfun2.sh 
    a=first
    a=first
    

    结果表明虽然脚本中定义了a=second,但是执行test函数之后,已经变成了first。为了解决脚本中变量和函数中变量的混淆,一般将函数中的变量定义为局部变量。




    3、使用函数

        <1>载入函数

           子shell中如果需要使用父shell中的函数,需要将函数加载至本shell

           加载方式

               source FUNCTION

               . FUNCTION      

           注:修改函数之后,必须重新载入shell才能生效

        <2>调用函数

           输入函数名再加参数即可 




    4、函数返回值return和exit的不同

        return:退出当前函数

            return :从函数中返回,用最后状态命令决定返回值

            return 0 :无错误返回。

            return 1-255 :有错误返回

        exit:退出当前脚本




    5、删除函数

        unset FUNCTION:删除函数

        set:查看所有定义的函数




    6、关于函数参数传递问题

        不知道大家有没有过这样的疑问,在函数中参数是如何传递的呢?因为在C语言中是这样定义一个函数的:int cmp(int a, int b),变量已经在函数中声明。但是shell中的函数并没有定义参数,这个过程是怎么完成的呢?小编刚开始也很疑惑,后来才明白shell的函数中通过函数位置变量和变量的引用就可以实现参数的传递。下面是一些示例以及传参的注意事项,大家可以参考一下。

    # 创建函数文件fun1,定义函数add
    function add() {
    local sum
     sum=$[$1+$2]
     echo $sum
    }
    # 编写脚本testfun1.sh
    #!/bin/bash
    source fun1
    add $1 $2
    ####执行脚本:
    [root@localhost ~/bin]#bash funadd.sh 1 2
    3
    

    脚本中的$1和$2是脚本引用命令行的位置变量,而函数中的$1和$2是引用脚本中的第一个变量和第二个变量,我们称其为函数位置变量。(实现传参的方法之一)

    看下面这种情况

    # 创建函数文件fun1,定义函数add
    function add() {
    local sum
     sum=$[$1+$2]
     echo $sum
    }
    # 编写脚本testfun1.sh
    #!/bin/bash
    source fun1
    read -p "Please input two figures:" a b    # 只添加此行
    add $1 $2
    # 执行脚本
    [root@localhost ~/bin]#bash funadd.sh 
    Please input two figures:1 2
    /root/bin/fun1: line 3: +: syntax error: operand expected (error token is "+")
    

    为什么这里会报错呢?因为在脚本中引用的位置变量$1和$2并不是1和2,也就是说$1和$2并没有值,函数中并没有引用到数值,计算结果当然是错的。从侧面也说明了read是无法引用位置变量的。

    那么,该怎么解决呢?答案如下

    #!/bin/bash
    source fun1
    read -p "Please input two figures:" a b
    add $a $b                          # 只改变此行
    [root@localhost ~/bin]#bash funadd.sh 
    Please input two figures:1 2
    3
    

    因为此时的$a和$b引用了命令行输入的1和2,而函数中又会调用这俩个值,结果计算正确。

    再看一种情况

    # 创建函数文件fun1,定义函数add
    function add() {
      local sum
      sum=$[$a+$b]
      echo $sum
    }
    # 编写脚本testfun1.sh
    #!/bin/bash
    source fun1
    read -p "Please input two figures:" a b
    add $1 $2   或者   add  $a $b   或者   add a b   # 三种情况
    # 执行脚本
    [root@localhost ~/bin]#bash funadd.sh 
    Please input two figures:1 2
    3
    

    小编已经实验过,这三种情况都能运行出结果,为了书写简便,都一一列举再在此处。但是大家知道这是为什么吗?因为函数直接引用了命令行的参数(实现传参的方法之二,弱类型编程语言特有的变量引用),也就不需要通过脚本来传参了。所以交互式输入的脚本中调用的函数如果没有使用$1和$2之类的字符来引用变量,而是直接使用了$a和$b之类的字符,那是此时传参的意义也就不存在了。

    不过在非交互式的脚本中调用的函数也可以直接调用脚本中的变量(实现传参的方法之三)


    再举一个例子加深一下印象

    # 创建函数文件fun3,定义函数string
    function string() {
    if [ $1 == ha ]; then
      echo "nihao"
    fi
    }
    # 编写脚本testfun3.sh
    #!/bin/bash
    #
    source fun3
    a=ha
    string $a
    unset a
    ####执行脚本
    [root@localhost ~]#bash testfun3.sh 
    nihao
    

    # 创建函数文件fun3,定义函数string
    function string() {
    if [ $a == ha ]; then
      echo "nihao"
    fi
    }
    # 编写脚本testfun3.sh
    #!/bin/bash
    #
    source fun3
    a=ha
    string $a  或者  string $1  或者  string
    unset a
    ####执行脚本
    [root@localhost ~]#bash testfun3.sh 
    nihao
    




    课后强化练习

    1、编写服务脚本/root/bin/testsrv.sh,完成如下要求

    (1) 脚本可接受参数:start, stop, restart, status

    (2) 如果参数非此四者之一,提示使用格式后报错退出

    (3) 如是start:则创建/var/lock/subsys/SCRIPT_NAME, 并显示“启动成功”

    考虑:如果事先已经启动过一次,该如何处理?

    (4) 如是stop:则删除/var/lock/subsys/SCRIPT_NAME, 并显示“停止完成”

    考虑:如果事先已然停止过了,该如何处理?

    (5) 如是restart,则先stop, 再start

    考虑:如果本来没有start,如何处理?

    (6) 如是status, 则如果/var/lock/subsys/SCRIPT_NAME文件存在,则显示“SCRIPT_NAMEis running...”

    如果/var/lock/subsys/SCRIPT_NAME文件不存在,则显示“SCRIPT_NAME is stopped...”

    其中:SCRIPT_NAME为当前脚本名

    # 创建函数文件testsrv,定义多个函数
    [root@localhost ~/bin/dir]#vim testsrv
    function start() {
        if [ -e /var/lock/subsys/testsrv.sh ]; then
          echo "Already start..."
        else
          touch /var/lock/subsys/testsrv.sh
          echo "start ok"
        fi
    }
    
    function stop() {
        if [ ! -e /var/lock/subsys/testsrv.sh ]; then
          echo "Already stop..."
        else
          rm -f /var/lock/subsys/testsrv.sh
          echo "stop ok"
        fi
    }
    
    function restart() {
        stop 
        start 
    }
    
    function status() {
      if [ -e /var/lock/subsys/testsrv.sh ]; then
        echo "testsrv.sh is running..."
      else
        echo "testsrv.sh is stopped..."
      fi
    }
    
    function quit() {
      exit 2
    }
    
    function again() {
      while [ $1 != start -a $1 != stop -a $1 != restart -a $1 != status ]; do
      read -p "Error,please enter again:" CHOICE
      done
    }       # 最后一个函数未使用,问题。
    
    # 编写脚本testsrv3.sh   
    source /root/bin/dir/testsrv
    cat << EOF
    four choices for you:
    start)
    stop)
    restart)
    status)
    EOF
    
    read -p "please input your choice(start|stop|restart|status|quit):" CHOICE
      while [ $CHOICE != "start" -a $CHOICE != "stop" -a $CHOICE != "restart" -a $CHOICE != "status" ]; do
        read -p "Error,please enter again:" CHOICE
       done
    case $CHOICE in 
    start)
      start 
      ;;
    stop) 
      stop
      ;;
    restart) 
      restart 
      ;;
    status) 
      status
      ;;
    quit)
      quit 
      ;;
    esac
    

    或者使用select

    # 函数不变,改写脚本
    #!/bin/bash
    source testsrv
    PS3="please input your choice:"
    select CHOICE in start stop restart status quit; do
      case $CHOICE in
      start)
        start
        ;;
      stop)
        stop 
        ;;
      restart)
        restart 
        ;;
      status)
        status
        ;;
      quit)
        quit 
        ;;
      *)
        echo "Error,please enter again..."
      esac
    done
    

    2、编写脚本/root/bin/copycmd.sh

    (1) 提示用户输入一个可执行命令名称;

    (2) 获取此命令所依赖到的所有库文件列表

    (3) 复制命令至某目标目录(例如/mnt/sysroot)下的对应路径下;

    如:/bin/bash ==> /mnt/sysroot/bin/bash

    /usr/bin/passwd==> /mnt/sysroot/usr/bin/passwd

    (4) 复制此命令依赖到的所有库文件至目标目录下的对应路径下:

    如:/lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64/ld-linux-x86-64.so.2

    (5)每次复制完成一个命令后,不要退出,而是提示用户键入新的要复制的命令,并重复完成上述功能;直到用户输入quit退出

    # 创建函数文件copycmd,定义多个函数
    function query() {
      ldd /usr/bin/$1
    }
    
    function copy() {
      mkdir /mnt/sysroot &> /dev/null
      local dir1
      dir1=/mnt/sysroot
      cp /usr/bin/$1 $dir1
      mkdir /mnt/sysroot/lib64 &> /dev/null
      local dir2
      dir2=/mnt/sysroot/lib64
      cp `ldd /usr/bin/ls | sed -r 's@[[:space:]]+.*=>?[[:space:]]?(.*)[[:space:]].*@\1@'` $dir2 &> /dev/null
    }
    
    function quit() {
      if [ $1 == quit ]; then
        exit
      fi
    }
    
    # 编写脚本copycmd4
    #!/bin/bash
    source copycmd
    PS3='Please input your option:'
    select option in run quit; do   # 列出俩个选项,是否运行或者退出,select自带循环功能
      case $option in                      退出即可,option是run和quit
      run)
        read -p "Input your cmd:" CMD   # 赋初值,否则无法与quit比较,直接错误                                                       
        until [[ $CMD == quit ]]; do
          if  which $CMD &> /dev/null; then
            query $CMD           # 调用函数
            copy $CMD            # 调用函数
          else
            read -p "Error,again:" CMD  
          fi
        read -p "Input your cmd:" CMD   # 纠正初值,与quit比较
        done
      ;;
      quit)
        quit $option
      ;;
      esac
    done
    




    展开全文
  • 系统调用和函数调用

    千次阅读 2015-11-24 13:42:25
    系统调用和函数调用1. 系统调用a. 什么是系统调用系统调用,通俗的讲,说的是操作系统提供给用户程序调用的一组“特殊”接口。用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务,比如用户可以通过文件...

    系统调用和函数调用


    1. 系统调用

    a. 什么是系统调用

    系统调用,通俗的讲,说的是操作系统提供给用户程序调用的一组“特殊”接口。用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务,比如用户可以通过文件系统相关的调用请求系统打开文件、关闭文件或读写文件,可以通过时钟相关的系统调用获得系统时间或设置定时器等。

    b. 系统调用的用途

    1. 控制硬件—系统调用往往作为硬件资源和用户空间的抽象接口,比如读写文件时用到的write/read调用。
    2. 设置系统状态或读取内核数据——因为系统调用是用户空间和内核的唯一通讯手段,所以用户设置系统状态,比如开/关某项内核服务(设置某个内核变量),或读取内核数据都必须通过系统调用。
    3. 进程管理—系统调用接口是用来保证系统中进程能以多任务在虚拟内存环境下得以运行。比如 fork、clone、execve、exit等

    c. 系统调用的实现

    Linux中实现系统调用利用了0x86体系结构中的软件中断。首先,用户程序为系统调用设置参数。其中一个参数是系统调用编号。参数设置完成后,程序执行“系统调用”指令。x86系统上的软中断由int产生。这个指令会导致一个异常:产生一个事件,这个事件会致使处理器切换到内核态并跳转到一个新的地址,并开始执行那里的异常处理程序。此时的异常处理程序实际上就是系统调用处理程序。它与硬件体系结构紧密相关。
    新地址的指令会保存程序的状态,计算出应该调用哪个系统调用,调用内核中实现那个系统调用的函数,恢复用户程序状态,然后将控制权返还给用户程序。系统调用是设备驱动程序中定义的函数最终被调用的一种方式。

    d. 系统调用的过程

    这里写图片描述

    e. 系统调用列表

    常见的系统调用有:connect用于连接远程主机getuid获取用户标志号fork()waitpid()等详情可以从下列网址获取:

    http://www.ibm.com/developerworks/cn/linux/kernel/syscall/part1/appendix.html

    2. 函数调用

    函数调用就相对熟悉多了。我们大一、大二编程所用的函数调用大多都是这里的函数调用,它运行在用户空间。
    它主要通过压栈操作来进行函数调用。
    例如,函数调用会执行下列操作:
    ⒈ 将帧指针压入栈中:pushebp
    ⒉ 使得帧指针等于栈指针:movebp,esp
    ⒊ 使栈指针自减,自减得到的内存地址应当能够(足够)用来存储被调用函数的本地状态:sub esp,0CCh


    3. 函数调用与系统调用的区别

    通俗的讲,函数调用是语言本身的一部分,而系统函数是内核提供给应用程序的接口,属于系统的一部分。函数调用是语言或应用程序的一部分,而系统调用是操作系统的一部分
    具体比较可参考下面的表格:(摘自网络)
    函数调用 系统调用
    在所有的ANSI C编译器版本中,C库函数是相同的 各个操作系统的系统调用是不同的
    它调用函数库中的一段程序(或函数) 它调用系统内核的服务
    与用户程序相联系 是操作系统的一个入口点
    在用户地址空间执行 在内核地址空间执行
    它的运行时间属于“用户时间” 它的运行时间属于“系统”时间
    属于过程调用,调用开销较小 需要在用户空间和内核上下文环境间切换,开销较大
    在C函数库libc中有大约300个函数 在UNIX中大约有90个系统调用
    典型的C函数库调用:system fprintfmalloc 典型的系统调用:chdir fork write brk;

    参考:
    1.https://zh.wikipedia.org/wiki/%E4%BB%BB%E5%8A%A1%E7%AE%A1%E7%90%86%E5%99%A8_(Windows%E5%85%83%E4%BB%B6)
    2. https://zh.wikipedia.org/wiki/%E5%A4%9A%E7%BA%BF%E7%A8%8B
    3. 《操作系统原理》
    4. http://www.kerneltravel.net/journal/iv/syscall.htm
    5. http://blog.csdn.net/yusiguyuan/article/details/23181327
    6. http://wenku.baidu.com/view/3e99ead6195f312b3169a580.html
    7. http://www.zhihu.com/question/19901763
    8. http://baike.baidu.com/view/65706.htm

    展开全文
  • 在阅读LDD3-第三章-字符设备驱动程序: struct file_operations scull_fops = {  .owner = THIS_MODULE,  .llseek = scull_llseek,  .read = scull_read,  .write ...
  • shell从函数文件中调用函数

    千次阅读 2014-06-23 11:06:33
    碰到一个shell中函数调用的小问题,记录一下。 shell函数有三种调用方式,一种是在文件前面定义函数,然后在下面直接调用;一种是通过载入shell,在shell直接调用;第三种是将函数写入文件,然后在其他shell...
  • 使用FFmpeg的av_read_frame函数后,每读完一个packet,必须调用av_packet_unref函数进行内存释放,否则会导致内存释泄漏。 在vs(博主所用的ffmpeg版本是3.4.2,vs版本是vs2015)编译运行如下代码: #include ...
  • 系统调用和函数调用区别

    千次阅读 2017-10-20 10:53:15
    系统调用和函数调用 1. 系统调用 a. 什么是系统调用 系统调用,通俗的讲,说的是操作系统提供给用户程序调用的一组“特殊”接口。用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务,比如用户可以...
  • shell脚本中函数调用

    万次阅读 多人点赞 2018-05-09 14:48:45
    函数 : 把一个功能封装起来,使用时直接调用函数名,这样的脚本好处:模块化,代码可读性强,扩展性方便函数的定义 在shell 有两种定义函数的语法格式,分别为: 函数名() { 命令序列 } 或者: function...
  • Linuxeventfd函数调用解析

    千次阅读 2013-07-08 11:39:22
    Linuxeventfd函数调用解析 eventfd 在内核版本,2.6.22以后有效。查看内核版本可以用命令 uname -r 。 #include int eventfd(unsigned int initval,int flags); 这个函数会创建一个 事件对象 (eventfd ...
  • C++ 函数调用和系统调用的区别

    千次阅读 2018-02-06 10:59:27
     所谓系统调用就是用户在程序中调用操作系统所提供的一个子功能,也就是系统API,系统调用可以被看做特殊的公共子程序。系统的各种共享资源都由操作系统统一掌管,因此在用户程序,凡是与资源有关的操作(如...
  • 系统调用和函数调用的区别

    千次阅读 2018-10-10 13:34:49
    系统调用与函数调用的区别 系统调用 1.使用INT和IRET指令,内核和应用程序使用的是不同的堆栈,因此存在堆栈的切换,从用户态切换到内核态,从而可以使用特权指令操控设备 2.依赖于内核,不保证移植性 3.在用户...
  • 系统调用与函数调用的区别

    千次阅读 2011-05-27 17:58:00
    函数库的某些函数调用了系统调用。函数库的函数可以没有调用系统调用,也可以调用多个系统调用。编程人员可以通过函数库调用系统调用。高级编程也可以直接采用int 0x80进入系统调用,而不必通过函数库作为中介。...
  • shell中函数的定义与调用

    千次阅读 2019-06-10 16:13:33
    1.什么是脚本函数? 首先得了解什么是脚本,脚本一般是网站的服务器端程序,主要用于动态网站的服务请求响应 故脚本函数就是写在脚本的特定功能函数了 脚本函数是把一个复杂的语句块定义成一个字符串的...
  • 汇编语言入门八:函数调用(二)

    千次阅读 2017-08-22 17:02:47
    上回说道,x86汇编专门提供了两个指令call和ret,用于实现函数调用的效果。实际上函数调用就是程序跳转,只是在跳转之前,CPU会保存当前所在的位置(即返回地址),当函数返回时,又可以从调用的位置恢复。返回...
  • 如何查看Python函数调用图 Ubuntu

    千次阅读 2017-12-07 14:49:42
    说明:有时候想看看Python的函数调用图,此时  pycallgraph 就显示出他的用途了。 安装 pycallgraph pip install pycallgraph 安装 graphviz,使用dot -v 验证安装,记得将 /usr/bin/ 设置到 PATH路径 ...
  • Python调用DLL函数

    千次阅读 2018-09-14 17:00:44
    最近研究了一下用Python调用dll,Python果然很神奇,代码不多既实现了从窗口创建到调用dll中函数的功能,程序也不复杂,最后打包成exe可直接执行的程序,测试过程先用Python3.5,但用PyInstaller打包后的程序在xp...
  • C#静态构造函数调用机制

    千次阅读 2017-02-21 22:40:06
    若一个类有静态构造函数,在首次实例化该类或任何的静态成员被引用时,.NET自动调用静态构造函数来初始化该类。注意是“首次”,即继续实例化该类时,不会调用该类的静态构造函数。1、实例化时调用静态构造函数 //...
  • 在3.1节分析的probe函数中,它的核心函数video_register_device的part3(v4l2-core/v4l2-dev.c文件),通过vdev->cdev->ops= &v4l2_fops;将字符设备的结构体cdev的file_operations函数集指向了v4l2_fops,如下所...
  • Lua中调用C函数

    千次阅读 2019-06-05 18:13:36
    Lua可以调用C函数的能力将极大的提高Lua的可...对于那些可被Lua调用的C函数而言,其接口必须遵循Lua要求的形式,即typedef int (lua_CFunction)(lua_StateL)。 简单说明一下,该函数类型仅仅包含一个表示Lua环境...
  • Linux Shell 之 Shell函数调用

    千次阅读 2015-07-28 16:03:57
    本文通过实例来分析shell函数使用。 一、Shell中函数的定义 为了方便程序和管理和模块化并减少代码的重复,函数是一个很好的可以共用的模块。而Shell中函数的定义有两种方法,如下: function fname() { ...
  • 字节流套接字上调用read和write输入或输出的字节数可能比请求的数量少,因为内核用于套接字的缓冲区是有限制的,需要调用者多次调用read或write函数。 提示:readn、writen和readline是对read和wirte的封装。 ...
  • Linux read系统调用

    千次阅读 2019-07-27 15:02:14
    1 read系统调用流程 本文内核版本:4.1.15 文件系统:ext3 read() vfs_read() rw_verify_area() __vfs_read() new_sync_read() generic_file_read_iter() do_generic_file_read() 2 ...
  • 函数调用模式4种方式详解

    千次阅读 2017-03-15 22:17:55
    函数调用模式: 函数模式特征:就是一个简单的函数调用,函数名前面没有任何的引导内容function foo(){} var func = function(){}foo(); func(); (function(){})(); this在函数模式的含义: this在函数表示全
  • c#构造函数调用含参构造函数

    千次阅读 2018-10-25 09:20:17
    话不多说,直接上代码: public class Test ...经过代码测试知道上面的含参构造函数在第一个构造函数中调用的,冒号后面为传的参数的值,构造函数没有返回值,这样便实现了实例化类时给类的属性赋值。
  • matlab的VideoReader函数 read函数

    千次阅读 2020-10-19 22:00:51
    来源 %取第一个文件夹里面的第一个...vidFrames = read(readerObj); %读取所有帧 numFrames = get(readerObj, 'NumberOfFrames'); % 帧数 % numFrames = readerObj.NumberOfFrames % 也可以获得帧数     &nb
  • 驱动接口函数调用过程

    千次阅读 2015-12-06 13:40:07
     首先我们来反汇编读驱动的程序: 跳转到__libc_read,发现他把r7赋值给3,3是传过去的参数,然后调用svc指令,进入内核态相应的入口: 接下来就已经进入内核态,入口函数中将存入r7的3取出。 然后加载一张表...
  • FlowDroid获取APK的函数调用

    千次阅读 热门讨论 2017-03-21 17:48:53
    使用FlowDroid这种工具最糟心的就是看不懂的...这篇博客和下一篇博客要介绍的是使用FlowDroid绘制APK的函数调用图(Call Graph)和每个函数的控制流图关于绘制dot图。这一篇博客先介绍获取函数调用图。 和画图相关的有

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 548,946
精华内容 219,578
关键字:

以下read函数的调用形式中