linux系统编程--文件IO

系统调用

什么是系统调用:

         由操作系统实现并提供给外部应用程序的编程接口。(Application Programming Interface,API)。是应用程序同系统之间数据交互的桥梁。

 

         C标准函数和系统函数调用关系。一个helloworld如何打印到屏幕。

 

 

C标准库文件IO函数。

fopen、fclose、fseek、fgets、fputs、fread、fwrite......

         r 只读、 r+读写

w只写并截断为0、 w+读写并截断为0

a追加只写、 a+追加读写

open/close函数  

函数原型:头文件 <unistd.h>

 

int open(const char *pathname, int flags);

int open(const char *pathname, int flags, mode_t mode);

int close(int fd);

常用参数

O_RDONLY、O_WRONLY、O_RDWR         

O_APPEND、O_CREAT、O_EXCL(判断文件是否存在)、 O_TRUNC(文件截断为0)、 O_NONBLOCK    

使用头文件:<fcntl.h>

open常见错误:

1. 打开文件不存在

2. 以写方式打开只读文件(打开文件没有对应权限)

3. 以只写方式打开目录

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>

int main()
{
    int fd = open("./test.txt",O_RDONLY);
    printf("fd = %d\n",fd);

    close(fd);

    return 0;
}

O_CREAT 才需要指定第三个参数,文件权限还需要依赖于掩码umask,mode  & ~umask

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>

int main()
{
    int fd = open("./text.txt",O_RDONLY | O_CREAT, 777); // 权限八进制,
    printf("fd = %d",fd);

    close(fd);

    return 0;
}

 

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>

int main()
{
    int fd = open("./textt.txt",O_RDONLY);
    printf("fd = %d",fd);
    printf("error: %d",errno);

    close(fd);

    return 0;
}

 

文件描述符:

PCB进程控制块

         可使用命令locate sched.h查看位置:        /usr/src/linux-headers-3.16.0-30/include/linux/sched.h                

struct task_struct { 结构体  

struct task_struct {
#ifdef CONFIG_THREAD_INFO_IN_TASK
    /*
     * For reasons of header soup (see current_thread_info()), this
     * must be the first element of task_struct.
     */
    struct thread_info        thread_info;
#endif
    /* -1 unrunnable, 0 runnable, >0 stopped: */
    volatile long            state;

    /*
     * This begins the randomizable portion of task_struct. Only
     * scheduling-critical items should be added above here.
     */
    randomized_struct_fields_start

    void                *stack;
    atomic_t            usage;
    /* Per task flags (PF_*), defined further below: */
    unsigned int            flags;
    unsigned int            ptrace;

#ifdef CONFIG_SMP
    struct llist_node        wake_entry;
    int                on_cpu;
#ifdef CONFIG_THREAD_INFO_IN_TASK
    /* Current CPU: */
    unsigned int            cpu;
#endif
    unsigned int            wakee_flips;
    unsigned long            wakee_flip_decay_ts;
    struct task_struct        *last_wakee;

    int                wake_cpu;
#endif
    int                on_rq;

    int                prio;
    int                static_prio;
    int                normal_prio;
    unsigned int            rt_priority;

    const struct sched_class    *sched_class;
    struct sched_entity        se;
    struct sched_rt_entity        rt;
#ifdef CONFIG_CGROUP_SCHED
    struct task_group        *sched_task_group;
#endif
    struct sched_dl_entity        dl;

#ifdef CONFIG_PREEMPT_NOTIFIERS
    /* List of struct preempt_notifier: */
    struct hlist_head        preempt_notifiers;
#endif

#ifdef CONFIG_BLK_DEV_IO_TRACE
    unsigned int            btrace_seq;
#endif

    unsigned int            policy;
    int                nr_cpus_allowed;
    cpumask_t            cpus_allowed;

#ifdef CONFIG_PREEMPT_RCU
    int                rcu_read_lock_nesting;
    union rcu_special        rcu_read_unlock_special;
    struct list_head        rcu_node_entry;
    struct rcu_node            *rcu_blocked_node;
#endif /* #ifdef CONFIG_PREEMPT_RCU */

#ifdef CONFIG_TASKS_RCU
    unsigned long            rcu_tasks_nvcsw;
    u8                rcu_tasks_holdout;
    u8                rcu_tasks_idx;
    int                rcu_tasks_idle_cpu;
    struct list_head        rcu_tasks_holdout_list;
#endif /* #ifdef CONFIG_TASKS_RCU */

    struct sched_info        sched_info;

    struct list_head        tasks;
#ifdef CONFIG_SMP
    struct plist_node        pushable_tasks;
    struct rb_node            pushable_dl_tasks;
#endif

    struct mm_struct        *mm;
    struct mm_struct        *active_mm;

    /* Per-thread vma caching: */
    struct vmacache            vmacache;

#ifdef SPLIT_RSS_COUNTING
    struct task_rss_stat        rss_stat;
#endif
    int                exit_state;
    int                exit_code;
    int                exit_signal;
    /* The signal sent when the parent dies: */
    int                pdeath_signal;
    /* JOBCTL_*, siglock protected: */
    unsigned long            jobctl;

    /* Used for emulating ABI behavior of previous Linux versions: */
    unsigned int            personality;

    /* Scheduler bits, serialized by scheduler locks: */
    unsigned            sched_reset_on_fork:1;
    unsigned            sched_contributes_to_load:1;
    unsigned            sched_migrated:1;
    unsigned            sched_remote_wakeup:1;
    /* Force alignment to the next boundary: */
    unsigned            :0;

    /* Unserialized, strictly 'current' */

    /* Bit to tell LSMs we're in execve(): */
    unsigned            in_execve:1;
    unsigned            in_iowait:1;
#ifndef TIF_RESTORE_SIGMASK
    unsigned            restore_sigmask:1;
#endif
#ifdef CONFIG_MEMCG
    unsigned            memcg_may_oom:1;
#ifndef CONFIG_SLOB
    unsigned            memcg_kmem_skip_account:1;
#endif
#endif
#ifdef CONFIG_COMPAT_BRK
    unsigned            brk_randomized:1;
#endif
#ifdef CONFIG_CGROUPS
    /* disallow userland-initiated cgroup migration */
    unsigned            no_cgroup_migration:1;
#endif

    unsigned long            atomic_flags; /* Flags requiring atomic access. */

    struct restart_block        restart_block;

    pid_t                pid;
    pid_t                tgid;

#ifdef CONFIG_CC_STACKPROTECTOR
    /* Canary value for the -fstack-protector GCC feature: */
    unsigned long            stack_canary;
#endif
    /*
     * Pointers to the (original) parent process, youngest child, younger sibling,
     * older sibling, respectively.  (p->father can be replaced with
     * p->real_parent->pid)
     */

    /* Real parent process: */
    struct task_struct __rcu    *real_parent;

    /* Recipient of SIGCHLD, wait4() reports: */
    struct task_struct __rcu    *parent;

    /*
     * Children/sibling form the list of natural children:
     */
    struct list_head        children;
    struct list_head        sibling;
    struct task_struct        *group_leader;

    /*
     * 'ptraced' is the list of tasks this task is using ptrace() on.
     *
     * This includes both natural children and PTRACE_ATTACH targets.
     * 'ptrace_entry' is this task's link on the p->parent->ptraced list.
     */
    struct list_head        ptraced;
    struct list_head        ptrace_entry;

    /* PID/PID hash table linkage. */
    struct pid_link            pids[PIDTYPE_MAX];
    struct list_head        thread_group;
    struct list_head        thread_node;

    struct completion        *vfork_done;

    /* CLONE_CHILD_SETTID: */
    int __user            *set_child_tid;

    /* CLONE_CHILD_CLEARTID: */
    int __user            *clear_child_tid;

    u64                utime;
    u64                stime;
#ifdef CONFIG_ARCH_HAS_SCALED_CPUTIME
    u64                utimescaled;
    u64                stimescaled;
#endif
    u64                gtime;
    struct prev_cputime        prev_cputime;
#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
    struct vtime            vtime;
#endif

#ifdef CONFIG_NO_HZ_FULL
    atomic_t            tick_dep_mask;
#endif
    /* Context switch counts: */
    unsigned long            nvcsw;
    unsigned long            nivcsw;

    /* Monotonic time in nsecs: */
    u64                start_time;

    /* Boot based time in nsecs: */
    u64                real_start_time;

    /* MM fault and swap info: this can arguably be seen as either mm-specific or thread-specific: */
    unsigned long            min_flt;
    unsigned long            maj_flt;

#ifdef CONFIG_POSIX_TIMERS
    struct task_cputime        cputime_expires;
    struct list_head        cpu_timers[3];
#endif

    /* Process credentials: */

    /* Tracer's credentials at attach: */
    const struct cred __rcu        *ptracer_cred;

    /* Objective and real subjective task credentials (COW): */
    const struct cred __rcu        *real_cred;

    /* Effective (overridable) subjective task credentials (COW): */
    const struct cred __rcu        *cred;

    /*
     * executable name, excluding path.
     *
     * - normally initialized setup_new_exec()
     * - access it with [gs]et_task_comm()
     * - lock it with task_lock()
     */
    char                comm[TASK_COMM_LEN];

    struct nameidata        *nameidata;

#ifdef CONFIG_SYSVIPC
    struct sysv_sem            sysvsem;
    struct sysv_shm            sysvshm;
#endif
#ifdef CONFIG_DETECT_HUNG_TASK
    unsigned long            last_switch_count;
#endif
    /* Filesystem information: */
    struct fs_struct        *fs;

    /* Open file information: */
    struct files_struct        *files;

    /* Namespaces: */
    struct nsproxy            *nsproxy;

    /* Signal handlers: */
    struct signal_struct        *signal;
    struct sighand_struct        *sighand;
    sigset_t            blocked;
    sigset_t            real_blocked;
    /* Restored if set_restore_sigmask() was used: */
    sigset_t            saved_sigmask;
    struct sigpending        pending;
    unsigned long            sas_ss_sp;
    size_t                sas_ss_size;
    unsigned int            sas_ss_flags;

    struct callback_head        *task_works;

    struct audit_context        *audit_context;
#ifdef CONFIG_AUDITSYSCALL
    kuid_t                loginuid;
    unsigned int            sessionid;
#endif
    struct seccomp            seccomp;

    /* Thread group tracking: */
    u32                parent_exec_id;
    u32                self_exec_id;

    /* Protection against (de-)allocation: mm, files, fs, tty, keyrings, mems_allowed, mempolicy: */
    spinlock_t            alloc_lock;

    /* Protection of the PI data structures: */
    raw_spinlock_t            pi_lock;

    struct wake_q_node        wake_q;

#ifdef CONFIG_RT_MUTEXES
    /* PI waiters blocked on a rt_mutex held by this task: */
    struct rb_root_cached        pi_waiters;
    /* Updated under owner's pi_lock and rq lock */
    struct task_struct        *pi_top_task;
    /* Deadlock detection and priority inheritance handling: */
    struct rt_mutex_waiter        *pi_blocked_on;
#endif

#ifdef CONFIG_DEBUG_MUTEXES
    /* Mutex deadlock detection: */
    struct mutex_waiter        *blocked_on;
#endif

#ifdef CONFIG_TRACE_IRQFLAGS
    unsigned int            irq_events;
    unsigned long            hardirq_enable_ip;
    unsigned long            hardirq_disable_ip;
    unsigned int            hardirq_enable_event;
    unsigned int            hardirq_disable_event;
    int                hardirqs_enabled;
    int                hardirq_context;
    unsigned long            softirq_disable_ip;
    unsigned long            softirq_enable_ip;
    unsigned int            softirq_disable_event;
    unsigned int            softirq_enable_event;
    int                softirqs_enabled;
    int                softirq_context;
#endif

#ifdef CONFIG_LOCKDEP
# define MAX_LOCK_DEPTH            48UL
    u64                curr_chain_key;
    int                lockdep_depth;
    unsigned int            lockdep_recursion;
    struct held_lock        held_locks[MAX_LOCK_DEPTH];
#endif

#ifdef CONFIG_UBSAN
    unsigned int            in_ubsan;
#endif

    /* Journalling filesystem info: */
    void                *journal_info;

    /* Stacked block device info: */
    struct bio_list            *bio_list;

#ifdef CONFIG_BLOCK
    /* Stack plugging: */
    struct blk_plug            *plug;
#endif

    /* VM state: */
    struct reclaim_state        *reclaim_state;

    struct backing_dev_info        *backing_dev_info;

    struct io_context        *io_context;

    /* Ptrace state: */
    unsigned long            ptrace_message;
    siginfo_t            *last_siginfo;

    struct task_io_accounting    ioac;
#ifdef CONFIG_TASK_XACCT
    /* Accumulated RSS usage: */
    u64                acct_rss_mem1;
    /* Accumulated virtual memory usage: */
    u64                acct_vm_mem1;
    /* stime + utime since last update: */
    u64                acct_timexpd;
#endif
#ifdef CONFIG_CPUSETS
    /* Protected by ->alloc_lock: */
    nodemask_t            mems_allowed;
    /* Seqence number to catch updates: */
    seqcount_t            mems_allowed_seq;
    int                cpuset_mem_spread_rotor;
    int                cpuset_slab_spread_rotor;
#endif
#ifdef CONFIG_CGROUPS
    /* Control Group info protected by css_set_lock: */
    struct css_set __rcu        *cgroups;
    /* cg_list protected by css_set_lock and tsk->alloc_lock: */
    struct list_head        cg_list;
#endif
#ifdef CONFIG_INTEL_RDT
    u32                closid;
    u32                rmid;
#endif
#ifdef CONFIG_FUTEX
    struct robust_list_head __user    *robust_list;
#ifdef CONFIG_COMPAT
    struct compat_robust_list_head __user *compat_robust_list;
#endif
    struct list_head        pi_state_list;
    struct futex_pi_state        *pi_state_cache;
#endif
#ifdef CONFIG_PERF_EVENTS
    struct perf_event_context    *perf_event_ctxp[perf_nr_task_contexts];
    struct mutex            perf_event_mutex;
    struct list_head        perf_event_list;
#endif
#ifdef CONFIG_DEBUG_PREEMPT
    unsigned long            preempt_disable_ip;
#endif
#ifdef CONFIG_NUMA
    /* Protected by alloc_lock: */
    struct mempolicy        *mempolicy;
    short                il_prev;
    short                pref_node_fork;
#endif
#ifdef CONFIG_NUMA_BALANCING
    int                numa_scan_seq;
    unsigned int            numa_scan_period;
    unsigned int            numa_scan_period_max;
    int                numa_preferred_nid;
    unsigned long            numa_migrate_retry;
    /* Migration stamp: */
    u64                node_stamp;
    u64                last_task_numa_placement;
    u64                last_sum_exec_runtime;
    struct callback_head        numa_work;

    struct list_head        numa_entry;
    struct numa_group        *numa_group;

    /*
     * numa_faults is an array split into four regions:
     * faults_memory, faults_cpu, faults_memory_buffer, faults_cpu_buffer
     * in this precise order.
     *
     * faults_memory: Exponential decaying average of faults on a per-node
     * basis. Scheduling placement decisions are made based on these
     * counts. The values remain static for the duration of a PTE scan.
     * faults_cpu: Track the nodes the process was running on when a NUMA
     * hinting fault was incurred.
     * faults_memory_buffer and faults_cpu_buffer: Record faults per node
     * during the current scan window. When the scan completes, the counts
     * in faults_memory and faults_cpu decay and these values are copied.
     */
    unsigned long            *numa_faults;
    unsigned long            total_numa_faults;

    /*
     * numa_faults_locality tracks if faults recorded during the last
     * scan window were remote/local or failed to migrate. The task scan
     * period is adapted based on the locality of the faults with different
     * weights depending on whether they were shared or private faults
     */
    unsigned long            numa_faults_locality[3];

    unsigned long            numa_pages_migrated;
#endif /* CONFIG_NUMA_BALANCING */

    struct tlbflush_unmap_batch    tlb_ubc;

    struct rcu_head            rcu;

    /* Cache last used pipe for splice(): */
    struct pipe_inode_info        *splice_pipe;

    struct page_frag        task_frag;

#ifdef CONFIG_TASK_DELAY_ACCT
    struct task_delay_info        *delays;
#endif

#ifdef CONFIG_FAULT_INJECTION
    int                make_it_fail;
    unsigned int            fail_nth;
#endif
    /*
     * When (nr_dirtied >= nr_dirtied_pause), it's time to call
     * balance_dirty_pages() for a dirty throttling pause:
     */
    int                nr_dirtied;
    int                nr_dirtied_pause;
    /* Start of a write-and-pause period: */
    unsigned long            dirty_paused_when;

#ifdef CONFIG_LATENCYTOP
    int                latency_record_count;
    struct latency_record        latency_record[LT_SAVECOUNT];
#endif
    /*
     * Time slack values; these are used to round up poll() and
     * select() etc timeout values. These are in nanoseconds.
     */
    u64                timer_slack_ns;
    u64                default_timer_slack_ns;

#ifdef CONFIG_KASAN
    unsigned int            kasan_depth;
#endif

#ifdef CONFIG_FUNCTION_GRAPH_TRACER
    /* Index of current stored address in ret_stack: */
    int                curr_ret_stack;

    /* Stack of return addresses for return function tracing: */
    struct ftrace_ret_stack        *ret_stack;

    /* Timestamp for last schedule: */
    unsigned long long        ftrace_timestamp;

    /*
     * Number of functions that haven't been traced
     * because of depth overrun:
     */
    atomic_t            trace_overrun;

    /* Pause tracing: */
    atomic_t            tracing_graph_pause;
#endif

#ifdef CONFIG_TRACING
    /* State flags for use by tracers: */
    unsigned long            trace;

    /* Bitmask and counter of trace recursion: */
    unsigned long            trace_recursion;
#endif /* CONFIG_TRACING */

#ifdef CONFIG_KCOV
    /* Coverage collection mode enabled for this task (0 if disabled): */
    enum kcov_mode            kcov_mode;

    /* Size of the kcov_area: */
    unsigned int            kcov_size;

    /* Buffer for coverage collection: */
    void                *kcov_area;

    /* KCOV descriptor wired with this task or NULL: */
    struct kcov            *kcov;
#endif

#ifdef CONFIG_MEMCG
    struct mem_cgroup        *memcg_in_oom;
    gfp_t                memcg_oom_gfp_mask;
    int                memcg_oom_order;

    /* Number of pages to reclaim on returning to userland: */
    unsigned int            memcg_nr_pages_over_high;
#endif

#ifdef CONFIG_UPROBES
    struct uprobe_task        *utask;
#endif
#if defined(CONFIG_BCACHE) || defined(CONFIG_BCACHE_MODULE)
    unsigned int            sequential_io;
    unsigned int            sequential_io_avg;
#endif
#ifdef CONFIG_DEBUG_ATOMIC_SLEEP
    unsigned long            task_state_change;
#endif
    int                pagefault_disabled;
#ifdef CONFIG_MMU
    struct task_struct        *oom_reaper_list;
#endif
#ifdef CONFIG_VMAP_STACK
    struct vm_struct        *stack_vm_area;
#endif
#ifdef CONFIG_THREAD_INFO_IN_TASK
    /* A live task holds one reference: */
    atomic_t            stack_refcount;
#endif
#ifdef CONFIG_LIVEPATCH
    int patch_state;
#endif
#ifdef CONFIG_SECURITY
    /* Used by LSM modules for access restriction: */
    void                *security;
#endif

    /*
     * New fields for task_struct should be added above here, so that
     * they are included in the randomized portion of task_struct.
     */
    randomized_struct_fields_end

    /* CPU-specific state of this task: */
    struct thread_struct        thread;

    /*
     * WARNING: on x86, 'thread_struct' contains a variable-sized
     * structure.  It *MUST* be at the end of 'task_struct'.
     *
     * Do not put anything below here!
     */
};
struct task_struct

 

 

 

每当执行一个程序,运行./a.out,它就会产生这样一个0-4G的虚拟内存地址空间 

 

文件描述符表

   结构体PCB 的成员变量file_struct *file 指向文件描述符表。

   从应用程序使用角度,该指针可理解记忆成一个字符指针数组,下标0/1/2/3/4...找到文件结构体。

   本质是一个键值对0、1、2...都分别对应具体地址。但键值对使用的特性是自动映射,我们只操作键不直接使用值。

   新打开文件返回文件描述符表中未使用的最小文件描述符。

 

STDIN_FILENO       0

STDOUT_FILENO  1

STDERR_FILENO      2

FILE结构体

主要包含文件描述符、文件读写位置、IO缓冲区三部分内容。

struct file {

                   ...

                   文件的偏移量;

                   文件的访问权限;

                   文件的打开标志;

                   文件内核缓冲区的首地址;

                   struct operations * f_op;

                   ...              

         };                        

查看方法:

         (1) /usr/src/linux-headers-3.16.0-30/include/linux/fs.h       

         (2) lxr:LXR( Linux超文本交叉代码检索工具)    

Linux超文本交叉代码检索工具LXR(Linux Cross Reference),是由挪威奥斯陆大学数学系Arne Georg Gleditsch和Per Kristian Gjermshus编写的。这个工具实际上运行在Linux或者UNIX平台下,通过对源代码中的所有符号建立索引,从而可以方便的检索任何一个符号,包括函数、外部变量、文件名、宏定义等等。不仅仅是针对Linux源代码,对于C语言的其他大型的项目,都可以建立其lxr站点,以提供开发者查询代码,以及后继开发者学习代码。
  目前的lxr是专门为Linux下面的Apache服务器设计的,通过运行perl脚本,检索在安装时根据需要建立的源代码索引文件,将数据发送到网络客户端的Web浏览器上。任何一种平台上的Web浏览器都可以访问,这就方便了习惯在Windows平台下工作的用户。
       关于lxr的英文网站为http://lxr.linux.no/,在中国Linux论坛http://www.linuxforum.net上有其镜象。

如何建立自己的LXR网站?
直接通过http://lxr.linux.no/lxr-0.3.tar.gz,下载lxr的tarball形式的安装包。
另外,因为lxr使用glimpse作为整个项目中文本的搜索工具,因此还需要下载glimpse,
网址在http://glimpse.cs.arizona.edu
,下载glimpse-4.12.6.bin.Linux-2.2.5-22-i686.tar.gz,
也可以使用更新的版本。 下载以后按照说明进行安装和配置,就可以建立自己的LXR网站。
如果你上网很方便,就可以直接从http://lxr.linux.no/网站查询你需要的各种源码信息。
 
目前,可用的lxr网址有:
linux源码浏览:http://lxr.free-electrons.com/
LXR介绍

 

     百度 lxr → lxr.oss.org.cn → 选择内核版本(如3.10) → 点击File Search进行搜索

                   → 关键字:“include/linux/fs.h” → Ctrl+F 查找 “struct file {”

→ 得到文件内核中结构体定义

                   → “struct file_operations”文件内容操作函数指针

                   → “struct inode_operations”文件属性操作函数指针

最大打开文件数

一个进程默认打开文件的个数1024。

命令查看ulimit -a 查看open files 对应值。默认为1024   

gec@ubuntu:~/myshare/文件IO$ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 3615
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 3615
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

 

可以使用ulimit -n 4096 修改

当然也可以通过修改系统配置文件永久修改该值,但是不建议这样操作。

cat /proc/sys/fs/file-max可以查看该电脑最大可以打开的文件个数。受内存大小影响。

gec@ubuntu:~/myshare/文件IO$ cat /proc/sys/fs/file-max 
91729

read/write函数

ssize_t read(int fd, void *buf, size_t count);

ssize_t write(int fd, const void *buf, size_t count);

 

read与write函数原型类似。使用时需注意:read/write函数的第三个参数。

 

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>

int main()
{
    char buf[1024] = {0};
    int ret = 0;
    int fd = open("./open.c",O_RDONLY);

    printf("fd = %d",fd);
    printf("error: %d",errno);

    while((ret= read(fd,buf,sizeof(buf))) != 0 ) {
        write(STDOUT_FILENO,buf,ret);
    
    }

    close(fd);

    return 0;
}

 

练习:编写程序实现简单的cp功能。

 

/*
 *./mycp src dst 命令行参数实现简单的cp命令
 */
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>

char buf[1024];

int main(int argc, char *argv[])
{
    int src, dst;
    int n;

    src = open(argv[1], O_RDONLY); //只读打开源文件
    if(src < 0){
        perror("open src error");
        exit(1);
    }
    //只写方式打开,覆盖原文件内容,不存在则创建,rw-r--r--
    dst = open(argv[2], O_WRONLY|O_TRUNC|O_CREAT, 0644);
    if(src < 0){
        perror("open dst error");
        exit(1);
    }
    while((n = read(src, buf, 1024))){
        if(n < 0){
            perror("read src error");
            exit(1);
        }
        write(dst, buf, n);  //不应写出1024, 读多少写多少
    }

    close(src);
    close(dst);

    return 0;
}

 

程序比较:如果一个只读一个字节实现文件拷贝,使用read、write效率高,还是使用对应的标库函数效率高呢?

用系统函数read和write 一次一个字节的读写操作,和标准库函数 fgetc, fputc 一次一个字节读写

从理论上来看,好像 fgetc, fputc 的执行效率要比 read,write ,因为标库函数 底层还是调用系统函数

#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>

#define N 1

int main(int argc, char *argv[])
{
    int fd, fd_out;
    int n;
    char buf[N];

    fd = open("dict.txt", O_RDONLY);
    if(fd < 0){
        perror("open dict.txt error");
        exit(1);
    }

    fd_out = open("dict.cp", O_WRONLY|O_CREAT|O_TRUNC, 0644);
    if(fd < 0){
        perror("open dict.cp error");
        exit(1);
    }

    while((n = read(fd, buf, N))){
        if(n < 0){
            perror("read error");
            exit(1);
        }
        write(fd_out, buf, n);
    }

    close(fd);
    close(fd_out);

    return 0;
}
系统调用read,write
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    FILE *fp, *fp_out;
    int n;

    fp = fopen("dict.txt", "r");
    if(fp == NULL){
        perror("fopen error");
        exit(1);
    }

    fp_out = fopen("dict.cp", "w");
    if(fp == NULL){
        perror("fopen error");
        exit(1);
    }

    while((n = fgetc(fp)) != EOF){
        fputc(n, fp_out);
    }

    fclose(fp);
    fclose(fp_out);

    return 0;
}
标库调用fgetc,fputc

但是实际运行 标库函数运行的效率明显高于系统调用,这是因为系统的一个预读入和缓输出的机制

标库函数用户区有一个默认 4096 字节的缓冲区,不会每一次调用都去触发底层的系统调用,而系统调用函数用户区没有缓冲区机制,它的缓冲区大小有我们在程序中指定,

所以一个字节一个字节的读写操作的时候,标库函数要比系统调用节省很多次数据从用户区到内核区的的拷贝,而这一块是很耗费时间的。

strace命令

shell中使用strace命令跟踪程序执行,查看调用的系统函数

缓冲区

read、write函数常常被称为Unbuffered I/O。指的是无用户及缓冲区。但不保证不使用内核缓冲区。

预读入缓输出

 

 

错误处理函数:

错误号:errno

perror函数:        void perror(const char *s);

strerror函数:        char *strerror(int errnum);

 还有一个函数 strerror() 了解一下

查看错误号:        

/usr/include/asm-generic/errno-base.h

/usr/include/asm-generic/errno.h
#include <unistd.h>        //read write
#include <fcntl.h>        //open close O_WRONLY O_RDONLY O_CREAT O_RDWR
#include <stdlib.h>        //exit
#include <errno.h>
#include <stdio.h>        //perror
#include <string.h>

int main(void)
{
    int fd;
#if 1
    //打开文件不存在
    fd = open("test", O_RDONLY | O_CREAT);
    if(fd < 0){
        printf("errno = %d\n", errno);
    //    perror("open test error");
        printf("open test error: %s\n" , strerror(errno));

        //printf("open test error\n");
        exit(1);
    }
#elif 0
    //打开的文件没有对应权限(以只写方式打开一个只有读权限的文件)
    fd = open("test", O_WRONLY);        //O_RDWR也是错误的
    if(fd < 0){
        printf("errno = %d\n", errno);
        perror("open test error");
        //printf("open test error\n");
        exit(1);
    }

#endif
#if 0
    //以写方式打开一个目录
    fd = open("testdir", O_RDWR);        //O_WRONLY也是错的    
    if(fd < 0){
        perror("open testdir error");
        exit(1);
    }
#endif

    return 0;
}

 

阻塞、非阻塞 

读常规文件是不会阻塞的,不管读多少字节,read一定会在有限的时间内返回。从终端设备或网络读则不一定如果从终端输入的数据没有换行符,调用read读终端设备就会阻塞如果网络上没有接收到数据包,调用read从网络读就会阻塞,至于会阻塞多长时间也是不确定的,如果一直没有数据到达就一直阻塞在那里。同样,写常规文件是不会阻塞的,而向终端设备或网络写则不一定

 

现在明确一下阻塞(Block)这个概念。当进程调用一个阻塞的系统函数时,该进程被置于睡眠(Sleep)状态,这时内核调度其它进程运行,直到该进程等待的事件发生了(比如网络上接收到数据包,或者调用sleep指定的睡眠时间到了)它才有可能继续运行。与睡眠状态相对的是运行(Running)状态,在Linux内核中,处于运行状态的进程分为两种情况:

 

正在被调度执行。CPU处于该进程的上下文环境中,程序计数器(eip)里保存着该进程的指令地址,通用寄存器里保存着该进程运算过程的中间结果,正在执行该进程的指令,正在读写该进程的地址空间。

 

就绪状态。该进程不需要等待什么事件发生,随时都可以执行,但CPU暂时还在执行另一个进程,所以该进程在一个就绪队列中等待被内核调度。系统中可能同时有多个就绪的进程,那么该调度谁执行呢?内核的调度算法是基于优先级和时间片的,而且会根据每个进程的运行情况动态调整它的优先级和时间片,让每个进程都能比较公平地得到机会执行,同时要兼顾用户体验,不能让和用户交互的进程响应太慢。

 

阻塞读终端:【block_readtty.c】

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

//hello worl        d \n

int main(void)
{
    char buf[10];
    int n;

    n = read(STDIN_FILENO, buf, 10);   // #define STDIN_FILENO 0   STDOUT_FILENO 1  STDERR_FILENO 2
    if(n < 0){
        perror("read STDIN_FILENO");
        //printf("%d", errno);
        exit(1);
    }
    write(STDOUT_FILENO, buf, n);
    
    return 0;
}

非阻塞读终端 【nonblock_readtty.c】

#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MSG_TRY "try again\n"

int main(void)
{
    char buf[10];
    int fd, n;

    fd = open("/dev/tty", O_RDONLY|O_NONBLOCK); //使用O_NONBLOCK标志设置非阻塞读终端
    if(fd < 0){
        perror("open /dev/tty");
        exit(1);
    }
tryagain:

    n = read(fd, buf, 10);   //-1  (1)  出错  errno==EAGAIN或者EWOULDBLOCK

    if(n < 0){
        //由于open时指定了O_NONBLOCK标志,read读设备,没有数据到达返回-1,同时将errno设置为EAGAIN或EWOULDBLOCK
        if(errno != EAGAIN){        //也可以是 if(error != EWOULDBLOCK)两个宏值相同
            perror("read /dev/tty");
            exit(1);
        }
        sleep(3);
        write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));
        goto tryagain;
    }
    write(STDOUT_FILENO, buf, n);
    close(fd);

    return 0;
}

 

非阻塞读终端和等待超时【nonblock_timeout.c】

#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#define MSG_TRY "try again\n"
#define MSG_TIMEOUT "time out\n"

int main(void)
{
    char buf[10];
    int fd, n, i;

    fd = open("/dev/tty", O_RDONLY|O_NONBLOCK); // 重新打开 终端设备,默认是阻塞的,指定非阻塞打开方式
    if(fd < 0){
        perror("open /dev/tty");
        exit(1);
    }
    printf("open /dev/tty ok... %d\n", fd);

    for (i = 0; i < 5; i++){
        n = read(fd, buf, 10);
        if(n > 0){    //说明读到了东西
            break;
        }
        if(errno != EAGAIN){   //EWOULDBLK  
            perror("read /dev/tty");
            exit(1);
        }
        sleep(1);
        write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));
    }

    if(i == 5){
        write(STDOUT_FILENO, MSG_TIMEOUT, strlen(MSG_TIMEOUT));
    }else{
        write(STDOUT_FILENO, buf, n);
    }

    close(fd);

    return 0;
}

 

注意,阻塞与非阻塞是对于文件而言的。而不是read、write等的属性。read终端,默认阻塞读。

 

总结read 函数返回值:  

1. 返回非零值:  实际read到的字节数

2. 返回-1:

    1):errno != EAGAIN (或!= EWOULDBLOCK)  read出错

    2):errno == EAGAIN (或== EWOULDBLOCK)  设置了非阻塞读,并且没有数据到达。

3. 返回0:读到文件末尾

lseek函数 

文件偏移

Linux中可使用系统函数lseek来修改文件偏移量(读写位置)

每个打开的文件都记录着当前读写位置,打开文件时读写位置是0,表示文件开头,通常读写多少个字节就会将读写位置往后移多少个字节。

但是有一个例外,如果以O_APPEND方式打开,每次写操作都会在文件末尾追加数据,然后将读写位置移到新的文件末尾。

lseek和标准I/O库的fseek函数类似,可以移动当前读写位置(或者叫偏移量)。

 

fseek的作用及常用参数。 SEEK_SET()、SEEK_CUR(当前位置)、SEEK_END(文件尾)

int fseek(FILE *stream, long offset, int whence);  成功返回0;失败返回-1

特别的:超出文件末尾位置返回0(返回0标志着成功,不会出错);往回超出文件头位置,返回-1(失败)

 

off_t lseek(int fd, off_t offset, int whence); 失败返回-1;成功:返回的值是较文件起始位置向后的偏移量

特别的:lseek允许超过文件结尾设置偏移量,文件会因此被拓展(必须要有IO操作之后,拓展才会生效)。

    lseek(fd, 99, SEEK_SET);
    write(fd, "s", 1); // 必须要有IO操作

 

注意文件“读”和“写”使用同一偏移位置。【lseek.c】

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

int main(void)
{
    int fd, n;
    char msg[] = "It's a test for lseek\n";
    char ch;

    fd = open("lseek.txt", O_RDWR|O_CREAT, 0644);
    if(fd < 0){
        perror("open lseek.txt error");
        exit(1);
    }

    write(fd, msg, strlen(msg));    //使用fd对打开的文件进行写操作,问价读写位置位于文件结尾处。

    lseek(fd, 0, SEEK_SET);         //修改文件读写指针位置,位于文件开头。 注释该行会,将读不到数据

    while((n = read(fd, &ch, 1))){
        if(n < 0){
            perror("read error");
            exit(1);
        }
        write(STDOUT_FILENO, &ch, n);   //将文件内容按字节读出,写出到屏幕
    }

    close(fd);

    return 0;
}

 

lseek常用应用:                     

1. 使用lseek拓展文件:write操作才能实质性的拓展文件。单lseek是不能进行拓展的。  

                  一般:write(fd, "a", 1);                                                                   

             od -tcx filename  查看文件的16进制表示形式

             od -tcd filename  查看文件的10进制表示形式      

                  

2. 通过lseek获取文件的大小:lseek(fd, 0, SEEK_END);             【lseek_test.c】

 

【最后注意】:lseek函数返回的偏移量总是相对于文件头而言。                

fcntl函数

改变一个【已经打开】的文件的 访问控制属性。

重点掌握两个参数的使用,F_GETFL 和 F_SETFL。【fcntl.c】

#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MSG_TRY "try again\n"

int main(void)
{
    char buf[10];
    int flags, n;

    flags = fcntl(STDIN_FILENO, F_GETFL); //获取stdin属性信息
    if(flags == -1){
        perror("fcntl error");
        exit(1);
    }
    flags |= O_NONBLOCK;
    int ret = fcntl(STDIN_FILENO, F_SETFL, flags);
    if(ret == -1){
        perror("fcntl error");
        exit(1);
    }

tryagain:
    n = read(STDIN_FILENO, buf, 10);
    if(n < 0){
        if(errno != EAGAIN){        
            perror("read /dev/tty");
            exit(1);
        }
        sleep(3);
        write(STDOUT_FILENO, MSG_TRY, strlen(MSG_TRY));
        goto tryagain;
    }
    write(STDOUT_FILENO, buf, n);

    return 0;
}

 

ioctl函数

对设备的I/O通道进行管理,控制设备特性。(主要应用于设备驱动程序中)。

 通常用来获取文件的【物理特性】(该特性,不同文件类型所含有的值各不相同) 【ioctl.c】

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>

int main(void)
{
    struct winsize size;

    if (isatty(STDOUT_FILENO) == 0)     //
        exit(1);

    if(ioctl(STDOUT_FILENO, TIOCGWINSZ, &size)<0) {
        perror("ioctl TIOCGWINSZ error");
        exit(1);
    }
    printf("%d rows, %d columns\n", size.ws_row, size.ws_col);

    return 0;
}

 

传入传出参数

传入参数: 

const 关键字修饰的 指针变量  在函数内部读操作。  char *strcpy(cnost char *src, char *dst);

传出参数:   

1. 指针做为函数参数

2. 函数调用前,指针指向的空间可以无意义,调用后指针指向的空间有意义,且作为函数的返回值传出 

3. 在函数内部写操作。

传入传出参数:

1. 调用前指向的空间有实际意义 2. 调用期间在函数内读、写(改变原值)操作 3.作为函数返回值传出。

扩展阅读:

关于虚拟4G内存的描述和解析:

一个进程用到的虚拟地址是由内存区域表来管理的,实际用不了4G。而用到的内存区域,会通过页表映射到物理内存。

所以每个进程都可以使用同样的虚拟内存地址而不冲突,因为它们的物理地址实际上是不同的。内核用的是3G以上的1G虚拟内存地址,

其中896M是直接映射到物理地址的,128M按需映射896M以上的所谓高位内存。各进程使用的是同一个内核。

首先要分清“可以寻址”和“实际使用”的区别。

其实我们讲的每个进程都有4G虚拟地址空间,讲的都是“可以寻址”4G,意思是虚拟地址的0-3G对于一个进程的用户态和内核态来说是可以访问的,而3-4G是只有进程的内核态可以访问的。并不是说这个进程会用满这些空间。

其次,所谓“独立拥有的虚拟地址”是指对于每一个进程,都可以访问自己的0-4G的虚拟地址。虚拟地址是“虚拟”的,需要转化为“真实”的物理地址。

好比你有你的地址簿,我有我的地址簿。你和我的地址簿都有1、2、3、4页,但是每页里面的实际内容是不一样的,我的地址簿第1页写着3你的地址簿第1页写着4,对于你、我自己来说都是用第1页(虚拟),实际上用的分别是第3、4页(物理),不冲突。

内核用的896M虚拟地址是直接映射的,意思是只要把虚拟地址减去一个偏移量(3G)就等于物理地址。同样,这里指的还是寻址,实际使用前还是要分配内存。而且896M只是个最大值。如果物理内存小,内核能使用(分配)的可用内存也小。

 

posted @ 2019-05-13 23:14  狂奔~  阅读(677)  评论(0编辑  收藏  举报