Linux系统编程【转】
转自:https://blog.csdn.net/majiakun1/article/details/8558308
一.Linux系统编程概论 1.1 系统编程基石 syscall: libc:标准C库、系统调用封装、线程库、基本应用工具 gcc: 1.2 模块接口 API:应用程序编程接口,源代码级别,能通过编译,由标准C语言定义,libc来实现 ABI:应用程序二进制接口,二进制级别,能正常运行,关注调用约定、字节序、寄存器使用、系统调用、链接、二进制格式等,很难实现 1.3 错误处理 <stdio.h> errno: perror(const char *): <string.h> char * strerror (int errnum); int strerror_r(int errnum, char *buf, size_t len); 二.文件管理 2.1 基本文件I/O 2.1.1 open系统调用 2.1.1.1 定义 #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int open(const char *name, int flags); int open(const char *name, int flags, mode_t mode); 2.1.1.2 flags参数 O_RDONLY:只读 O_WRONLY:只写 O_RDWR:读写 其它O_XXX:APPEND, ASYNC, CREAT, DIRECT, DIRECTORY, EXCL, LARGEFILE, NOCTTY, NOFOLLOW, NONBLOCK, SYNC, TRUNC 2.1.1.3 mode参数 文件权限,例如0777,仅当flags包含标志O_CREAT时需要提供,也是必须提供的 2.1.1.4 creat函数 int creat(const char *name, int mode); 等价于 open(name, O_WRONLY | O_CREAT | O_TRUNC, mode); 2.1.2 read系统调用 2.1.2.1 定义 #include <unistd.h> ssize_t read(int fd, void *buf, size_t len); 2.1.2.2 返回值 ret = read(fd, buf, len) 1)ret == len:调用正常,结果和预期一致 2)0< ret < len:信号打断了读取过程、读取中发生错误、已经到达文件末尾,处理的办法是继续读取剩余的字节,更新buf和len 3)ret == 0:已经到达文件末尾 4)ret == -1 errno == EINTR:表示读入字节之前收到了一个信号,可以重新调用 errno == EAGAIN:在非阻塞模式下发生,表示无数据可读 other:发生了严重的错误 5)ret无返回值:阻塞了 2.1.2.3 阻塞读取示例 while(len != 0 && (ret = read(fd, buf, len))) { if(ret == -1) { if(errno == EINTR) continue; perror("read"); break; } len -= ret; buf += ret; } 2.1.2.4 非阻塞读取示例 char buf[BUFSIZ]; ssize_t nr; do { nr = read (fd, buf, BUFSIZ); if(nr == -1) { if(errno == EINTR) continue; if(errno == EAGAIN) { /* resubmit later */ } else { perror("read"); break; } } len -= ret; buf += ret; } 2.1.2.5 pread:从指定的偏移量开始读取,不改变当前文件偏移量指针,可避免竞态的lseek调用 #include <unistd.h> ssize_t pread(int fd, void *buf, size_t len, off_t pos); 2.1.3 write系统调用 2.1.3.1 定义 #include <unistd.h> ssize_t write(int fd, const char *buf, size_t count); 2.1.3.2 示例代码 while(len != 0 && (ret = write(fd, buf, len))) { if(ret == -1) { if(errno == EINTR) continue; perror("write"); break; } len -= ret; buf += ret; } 2.1.3.3 O_APPEND:适用于日志类型的应用 2.1.3.4 延迟写 内核设定了缓冲数据刷新的超时时间,由/proc/sys/vm/dirty_expire_centisecs定义 2.1.3.5 立刻写 1)int fsync(int fd):刷新文件fd的数据和元数据 2)int fdatasync(int fd):仅刷新文件fd的数据 3)void sync(void):刷新所有的缓冲数据,较耗时 4)O_SYNC,O_DSYNC,O_RSYNC: 2.1.3.6 O_DIRECT:忽略内核缓冲机制,最小化I/O管理,完全由用户处理请求长度,缓冲区对齐,文件偏移是扇区整数倍,例如数据库系统 2.1.3.7 pwrite:从指定的偏移量开始写,不改变当前文件偏移量指针,可避免竞态的lseek调用 #include <unistd.h> ssize_t pwrite(int fd, void *buf, size_t len, off_t pos); 2.1.4 close系统调用 #include <unistd.h> int close(int fd); 2.1.5 lseek系统调用 #include <sys/types.h> #include <unistd.h> off_t lseek(int fd, off_t pos, int origin); origin:SEEK_SET, SEEK_CUR, SEEK_END pos = 0时 origin == SEEK_SET:文件偏移量指向文件起始位置 origin == SEEK_CUR:返回当前文件偏移量 origin == SEEK_END:文件偏移量指向文件末尾 2.1.6 truncate & ftruncate 系统调用 截短文件,成功调用返回文件长度 #include <sys/types.h> #include <unistd.h> int ftruncate(int fd, off_t len); int truncate(const char *name, off_t len); 2.1.7 select & poll & epoll 系统调用 I/O多路复用,若有文件描述符准备好时通知我,没有就睡眠 2.1.7.1 select 1)定义 #include <sys/types.h> #include <sys/time.h> #include <unistd.h> int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); FD_CLR(int fd, fd_set *set); FD_ISSET(int fd, fd_set *set); FD_SET(int fd, fd_set *set); FD_ZERO(fd_set *set); 2)参数 n:fd_set中最大的fd + 1(不太合理,导致被poll取代) 3)返回值 成功时返回就绪的文件描述符数目 失败: EBADF:某文件描述符非法 EINTR:等待时捕获了一个信号,可以重新发起调用 EINVAL:参数n是负数,或者时间timeout不合法 ENOMEM:没有足够的内存完成请求 4)示例程序 #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/time.h> int main() { struct timeval timeout; fd_set set; int ret; timeout.tv_sec = 3; timeout.tv_usec = 0; FD_ZERO(&set); FD_SET(STDIN_FILENO, &set); ret = select(STDIN_FILENO + 1, &set, NULL, NULL, &timeout); if(ret < 0) perror("select"); else { if(FD_ISSET(STDIN_FILENO, &set)) { //read } } return 0; } 5)可利用select实现sleep 6)pselect int pselect(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask); 精确到纳秒,不修改timeout,设置阻塞的信号 2.1.7.2 poll 1)定义 #include <sys/poll.h> struct pollfd { int fd; //文件描述符 short events; //监视的事件 short revents; //发生的事件 } int poll(struct pollfd *fds, unsigned int nfds, int timeout); 2)事件 POLLIN、POLLRDNORM、POLLRDBAND、POLLPRI、POLLOUT、POLLWRNORM、POLLBAND、POLLMSG 3)ppoll int ppoll(struct pollfd *fds, unsigned int nfds, int timeout, const sigset_t *sigmask); 2.1.7.3 epoll 解决select & poll性能问题,适合同时监视大量文件描述符,先初始化,然后增加、删除,最后等待,分离了三者 1)定义 #include <sys/epoll.h> int epoll_create(int size); int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); 2.2 缓冲文件I/O 2.2.1 打开文件 FILE *fopen(const char *name, const char *mode); FILE *fdopen(int fd, const char *mode); 2.2.2 关闭文件 int fclose(FILE *stream); int fcloseall(); 2.2.3 读文件 2.2.3.1 字节读 int fgetc(FILE *stream); int ungetc(int c, FILE *stream); 2.2.3.2 行读取 int fgets(char *str, int size, FILE *stream):读取size-1个字节至str中,遇到换行符时,'\n'被存入str中 2.2.3.3 块读取 int fread(void *buf, size_t size, size_t nr, FILE *stream):从流stream中读入nr个元素,每个元素大小是size,返回读入的元素个数 2.2.4 写文件 2.2.4.1 字节写 int fputc(FILE *stream); int unputc(int c, FILE *stream); 2.2.4.2 行写 int fputs(const char *str, FILE *stream); 2.2.4.3 块写 int fwrite(void *buf, size_t size, size_t nr, FILE *stream); 2.2.5 定位文件 int fseek(FILE *stream, long offset, int whence):移动文件指针到指定位置 int fsetpos(FILE *stream, fpos_t *pos):跨平台 int fgetpos(FILE *stream, fpos_t *pos) int ftell(FILE *stream):返回当前文件位置 int rewind(FILE *stream):将文件指针置为初始位置 2.2.6 刷新文件 int fflush(FILE *stream):刷新用户缓冲区数据,不能刷新内核缓冲区数据 int fileno(FILE *stream):获取文件描述符fd,要先调用fflush 2.2.7 错误处理 int ferror(FILE *stream):测试是否有错误 int feof(FILE *stream):测试是否到达文件末尾 int clearerr(FILE *stream):清除错误标志 2.2.8 缓冲控制 int setvbuf(FILE *stream, char *buf, int mode, size_t size) 必须在紧邻fopen之后调用 mode:_IONBF(无缓冲),_IOLBF(行缓冲),_IOFBF(块缓冲) 2.2.9 文件锁 int flockfile(FILE *stream); int funlockfile(FILE *stream); int ftrylockfile(FILE *stream); 以上的流操作函数均是lock版,系统也提供了_unlock版 2.3 高级文件I/O 2.3.1 散列聚集I/O #include <sys/uio.h> ssize_t readv(int fd, const struct *iov, int count); ssize_t writev(int fd, const struct *iov, int count); 单次向量I/O代替多次线性I/O,降低了系统调用次数,避免了竞态 2.3.2 直接文件I/O 1)定义 #include <sys/mman.h> void *mmap(void *addr, size_t len, int port, int flags, int fd, off_t offset); int munmap(void *addr, size_t len); void * mremap (void *addr, size_t old_size,size_t new_size, unsigned long flags); int mprotect (const void *addr, size_t len, int prot); int msync (void *addr, size_t len, int flags); int madvise (void *addr, size_t len, int advice); 2)addr:告诉内核最佳映射地址,不是强制,一般置为0,调用返回映射地址 3)port:权限,PORT_READ,PORT_WRITE,PORT_EXEC 4)flags MAP_FIXED:addr是强制的 MAP_PRIVATE:映射区是私有的,写时拷贝 MAP_SHARED:映射区是共享的 5)页对齐:addr和len必须是页对齐的 long page_size = sysconf(_SC_PAGESIZE); //最好选择 int page_size = getpagesize ( ); int page_size= PAGE_SIZE ; 2.3.3 文件I/O提示 1)posix fadvise #include <fcntl.h> int posix_fadvise (int fd, off_t offset, off_t len, int advice); 2)readahead ssize_t readahead (int fd, off64_t offset, size_t count); 2.3.4 异步文件I/O #include <aio.h> /* asynchronous I/O control block */ struct aiocb { int aio_filedes; /* file descriptor */ int aio_lio_opcode; /* operation to perform */ int aio_reqprio; /* request priority offset */ volatile void *aio_buf; /* pointer to buffer */ size_t aio_nbytes; /* length of operation */ struct sigevent aio_sigevent; /* signal number and value */ /* internal, private members follow... */ }; int aio_read (struct aiocb *aiocbp); int aio_write (struct aiocb *aiocbp); int aio_error (const struct aiocb *aiocbp); int aio_return (struct aiocb *aiocbp); int aio_cancel (int fd, struct aiocb *aiocbp); int aio_fsync (int op, struct aiocb *aiocbp); int aio_suspend (const struct aiocb * const cblist[], int n, const struct timespec *timeout); Linux仅仅支持设置了O_DIRECT标志的异步操作 2.4 文件属性 2.4.1 inode:标识一个文件,在一个文件系统中是唯一的,既是linux内核虚拟对象,也是外存物理对象 2.4.2 获取文件属性 #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> int stat(const char *path, struct stat *buf);传入文件名 int fstat(int fd, struc stat *buf);传入文件描述符 int lstat(const char *path, struct stat *buf);返回链接文件本身 struct stat { dev_t st_dev; /* ID of device containing file */ ino_t st_ino; /* inode number */ mode_t st_mode; /* permissions */ nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ dev_t st_rdev; /* device ID (if special file) */ off_t st_size; /* total size in bytes */ blksize_t st_blksize; /* blocksize for filesystem I/O */ blkcnt_t st_blocks; /* number of blocks allocated */ time_t st_atime; /* last access time */ time_t st_mtime; /* last modification time */ time_t st_ctime; /* last status change time */ }; 2.4.3 设置文件权限 #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> int chmod(const char *path, mode_t mode); int fchmod(int fd, mode_t mode); 2.4.4 设置文件所有者 #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> int chown(const char *path, uid_t owner, gid_t group); int fchown(int fd, uid_t owner, gid_t group); int lchown(const char *path, uid_t owner, gid_t group); 2.4.5 扩展属性 #include <unistd.h> #include <sys/types.h> #include <sys/xattr.h> 1)命名空间:system,security,trusted,user 2)获取文件扩展属性 int getxattr(const char *path, const char *key, void *value, size_t size); int fgetxattr(int fd, const char *key, void *value, size_t size); int lgetxattr(const char *path, const char *key, void *value, size_t size); 3)设置文件扩展属性 int setxattr(const char *path, const char *key, void *value, size_t size, int flags); int fsetxattr(int fd, const char *key, void *value, size_t size, int flags); int lsetxattr(const char *path, const char *key, void *value, size_t size, int flags); 4)列举文件扩展属性 int listxattr(const char *path, char *list, size_t size); int flistxattr(int fd, char *list, size_t size); int llistxattr(const char *path, char *list, size_t size); 4)删除文件扩展属性 int removexattr(const char *path, char *key); int fremovexattr(int fd, char *key); int lremovexattr(const char *path, char *key); 2.5 目录管理 2.5.1 获取当前目录 #include <unistd.h> char *getcwd(char *buf, size_t size); 2.5.2 更改目录 #include <unistd.h> int chdir(const char *path); int fchdir(int fd); 2.5.3 创建目录 #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> int mkdir(const char *path, mode_t mode); 2.5.4 删除目录 #include <unistd.h> int rmdir(const char *path); 2.5.5 读取目录 #include <unistd.h> #include <sys/types.h> #include <dirent.h> DIR *opendir(const char *path); struct dirent *readdir(DIR *dir); int closedir(DIR *dir); 2.5.5 文件链接 #include <unistd.h> 1)硬链接 int link(const char *oldpath, const char *newpath); 2)符号链接 int symlink(const char *oldpath, const char *newpath); 3)解除链接 int unlink(const char *path); 2.5.6 文件复制移动删除 1)复制:无系统级支持,自己实现 2)移动 #include <stdio.h> int rename(const char *oldpath, const char *newpath); 3)删除 #include <stdio.h> int remove(const char *path); 2.6 设备文件 2.6.1 特殊设备文件 1) /dev/null:空设备,忽略所有写请求,读请求返回EOF 2) /dev/zero:零设备,忽略所有写请求,读请求返回0 3) /dev/full:满设备,写请求返回ENOSPC,读请求返回0 4) /dev/urandom:随机数生成设备,优于/dev/random 2.6.2 设备控制 #include <stdio.h> #include <sys/ioctl.h> #include <sys/ioctl.h> int ioctl(int fd, int request, ...); 2.7 监视文件 #include <inotify.h> inotify,监视文件被创建、打开、读取、写入、删除等操作 2.7.1 初始化 int inotify_init(); 2.7.2 增加监视 int inotify_add_watch(int fd, const char *path, uint32_t mask); 2.7.3 inotify 事件 struct inotify_event { int wd; /* watch descriptor */ uint32_t mask; /* mask of events */ uint32_t cookie; /* unique cookie */ uint32_t len; /* size of ’name’ field */ char name[]; /* null-terminated name */ }; 2.7.4 读取inotify 事件:read 2.7.5 删除inotify 事件 int inotify_rm_watch (int fd, uint32_t wd); 2.7.6 退出inotify:close 三.进程管理 进程创建fork和加载exec分离,是仅次于文件的基本抽象概念 3.1 获取进程ID #include <sys/types.h> #include <unistd.h> pid_t getpid(void); pid_t getppid(void); 1)PID在某个时刻是唯一的 2)进程0:idle进程,是所有其他进程的祖先,在系统初始化时由kernel自身从无到有创建。(过程集中在start_kernel函数内),数据成员大部分是静态定义的,即由预先定义好的(如上)INIT_TASK, INIT_MM等宏初始化。 3)进程1:init进程,由idle进程调用kernel_thread创建的 3.2 进程创建fork #include <sys/types.h> #include <unistd.h> pid_t fork(void); pid_t pid; pid = fork(); if(pid == -1) { perror("fork"); } else if(pid == 0) { //父进程代码 } else { //子进程代码 if( execlp("gedit", "gedit", "001.txt", NULL) == -1) { perror("execl"); exit(EXIT_FAILURE); } } 3.3 进程加载exec #include <unistd.h> int execl(const char *path, const char *arg, ...):加载指定路径的程序,参数列表必须以NULL结束 int execlp(const char *path, const char *arg, ...):在PATH环境变量中查找程序 int execle(const char *path, const char *arg, ..., char * const envp[]):提供给新进程环境变量 int execv(const char *path, char * const argv[]); int execvp(const char *path, char * const argv[]); int execve(const char *path, char * const argv[], char * const envp[]); l表示以列表方式提供参数,v表示以数组方式提供参数,p表示在PATH环境变量中查找程序,e表示提供给新进程环境变量 3.4 进程终止exit #include <stdlib.h> void exit(int status); EXIT_SUCCESS表示成功,EXIT_FAILURE表示失败 1)_exit #include <unistd.h> void _exit(int status); 2)at_exit #include <stdlib.h> int at_exit(void (*function)(void )); 3)SIGCHLD:当进程终止时,内核会向其父进程发送信号SIGCHLD 4)wait #include <sys/types.h> #include <sys/wait.h> pid_t wait(int *status); 返回已终止子进程PID,返回-1表示出错 5)waitpid #include <sys/types.h> #include <sys/wait.h> pid_t waitpid (pid_t pid, int *status, int options); 功能更强大 6)system #include <stdlib.h> int systme(const char *command); 创建并等待新进程 int my_system (const char *cmd) { int status; pid_t pid; pid = fork ( ); if (pid == -1) return -1; else if (pid == 0) { const char *argv[4]; argv[0] = ”sh”; argv[1] = ”-c”; argv[2] = cmd; argv[3] = NULL; execv (”/bin/sh”, argv); exit (-1); } if (waitpid (pid, &status, 0) == -1) return -1; else if (WIFEXITED (status)) return WEXITSTATUS (status); return -1; } 3.5 进程权限控制 3.5.1 用户ID 1) 实际用户ID:登陆进程使用的ID 2) 有效用户ID:检查进程权限过程中使用的ID 3) 保存设置用户ID:执行suid前的有效用户ID 4) 文件系统用户ID: 3.5.2 设置用户ID #include <unistd.h> #include <sys/types.h> int setuid(uid_t uid); int setgid(gid_t gid); int seteuid(uid_t uid); int setegid(gid_t gid); 设置当前进程的有效用户ID 3.5.3 获取用户ID #include <unistd.h> #include <sys/types.h> int getuid(); int getgid(); int geteuid(); int getegid(); 3.5.4 会话 登陆进程会为新用户创建会话,会话首进程(shell)pid作为会话ID,便于作业控制,可以发信号终止会话首进程的所有子进程 #include <unistd.h> 1) 创建一个会话: pid_t setsid(void); 2) 获取会话ID: pid_t getsid (pid_t pid); 3) 设置进程组ID: int setpgid (pid_t pid, pid_t pgid); 3.6 守护进程 #include <unistd.h> int daemon(int nochdir, int noclose); #include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <unistd.h> #include <linux/fs.h> int main (void) { pid_t pid; int i; pid = fork( ); /* create new process */ if (pid == -1) return -1; else if (pid != 0) exit (EXIT_SUCCESS); if (setsid ( ) == -1) /* create new session and process group */ return -1; if (chdir (”/”) == -1) /* set the working directory to the root directory */ return -1; for (i = 0; i < NR_OPEN; i++) /* close all open files--NR_OPEN is overkill,but works */ close (i); /* redirect fd’s 0,1,2 to /dev/null */ open (”/dev/null”, O_RDWR); /* stdin */ dup (0); /* stdout */ dup (0); /* stderror */ /* do its daemon thing... */ return 0; } 3.7 进程调度 3.7.1 让出处理器 #include <sched.h> int sched_yield(void); 3.7.2 进程优先级 #include <unistd.h> int nice(int inc); 在当前优先级基础上加inc,只有拥有CAP_SYS_NICE能力才能使用负值inc增加优先级,否则只能降低优先级 #include <sys/time.h> #include <sys/resource.h> int getpriority (int which, int who); int setpriority (int which, int who, int prio); 3.7.3 处理器亲和度 #include <sched.h> typedef struct cpu_set_t; size_t CPU_SETSIZE; void CPU_SET (unsigned long cpu, cpu_set_t *set); void CPU_CLR (unsigned long cpu, cpu_set_t *set); int CPU_ISSET (unsigned long cpu, cpu_set_t *set); void CPU_ZERO (cpu_set_t *set); int sched_setaffinity (pid_t pid, size_t setsize,const cpu_set_t *set); int sched_getaffinity (pid_t pid, size_t setsize,const cpu_set_t *set); 3.7.4 进程调度策略 #include <sched.h> struct sched_param { /* ... */ int sched_priority; /* ... */ }; int sched_getscheduler (pid_t pid); int sched_setscheduler (pid_t pid, int policy,const struct sched_param *sp); int sched_getparam (pid_t pid, struct sched_param *sp); int sched_setparam (pid_t pid, const struct sched_param *sp); int sched_rr_get_interval (pid_t pid, struct timespec *tp); SCHED_FIFO:先进先出 SCHED_RR:轮转 SCHED_OTHER:普通 3.7.5 资源限制 #include <sys/time.h> #include <sys/resource.h> struct rlimit { rlim_t rlim_cur; /* soft limit */ rlim_t rlim_max; /* hard limit */ }; int getrlimit (int resource, struct rlimit *rlim); int setrlimit (int resource, const struct rlimit *rlim); RLIMIT AS,CORE,CPU,DATA,FSIZE,LOCKS,MEMLOCK,MSGQUEUE,NICE,NOFILE,NPROC,RSS,RTPRIO,SIGPENDING,STACK 四.内存管理 4.1 基本存储/释放 4.1.1 malloc #include <stdlib.h> void *malloc(size_t size); 成功会返回指向size大小内存区域的首指针,错误返回NULL,设置errno为ENOMEM 4.1.2 calloc #include <stdlib.h> void *calloc(size_t nr, size_t size); 分配数组,会用0初始化 void *xmalloc0(size_t size) { void *p = NULL; p = calloc(1, size); if(p == NULL) { perror("malloc"); exit(EXIT_FAILURE); } return p; } 4.1.3 realloc #include <stdlib.h> void *realloc(void *ptr, size_t size); 重新分配,可改变已分配内存区域的大小,主要用途是变小 4.1.4 free #include <stdlib.h> void free(void *ptr); ptr必须是malloc/calloc/realloc的返回值 4.2 字节对齐 #include <stdlib.h> int posix_memalign (void **memptr,size_t alignment,size_t size); 成功时返回size字节的动态内存,且以alignment,alignment必须是2的幂 4.3 匿名映射 void *p; p = mmap (NULL, 512 * 1024, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (p == MAP_FAILED) perror (”mmap”); else /* p points at 512 KB of anonymous memory... */ ... int ret; ret = munmap(p, 512 * 1024); if(ret) perror("munmap"); 适合大内存区域分配 4.4 设置内存分配参数 #include <malloc.h> int mallopt (int param, int value); size_t malloc_usable_size (void *ptr); int malloc_trim (size_t padding); 4.5 调试内存分配 #include <malloc.h> struct mallinfo mallinfo (void); /* all sizes in bytes */ struct mallinfo { int arena; int ordblks; int smblks; int hblks; int hblkhd; int usmblks; int fsmblks; int uordblks; int fordblks; int keepcost; }; #include <malloc.h> void malloc_stats (void); 4.6 栈分配&变长数组 #include <alloca.h> void * alloca (size_t size);
【作者】sky
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.