介绍系统编程中线程的概念以及线程的特点。
文件与文件夹 下面几个函数主要是获取文件夹的属性。
1 2 3 4 5 6 7 8 9 #include <sys/stat.h> int stat (const char * path,struct stat* buf) ;int fstat (int fd,struct stat* buf) ;int lstat (const char * path,struct stat* buf) ;int fstatat (int dirfd,const char * path,struct stat* buf,int flags) ;
上面四个函数的返回值都是0表示成功,返回-1表示失败。
系统IO 高级IO 进程环境 首先我们需要知道退出函数:
1 2 3 4 5 6 #incldue <stdlib.h> void exit (int status) ;void _Exit(int status);#include <unistd.h> void _exit(int status);
这里需要注意的是 exit()
函数是退出进程,return
语句是返回函数 pthread_exit()
是退出线程。所以我们需要注意的是,需要选择正确的函数
上述的三个函数使用了两种头文件,这是因为它们不是同一个标准总说明的。
1 2 3 4 #include <stdlib.h> int atexit (void (*func)(void )) ;
下面主要是动态内存分配的函数
1 2 3 4 5 6 #include <stdlib.h> void * malloc (size_t size) ;void * calloc (size_t nmemb,size_t size) ;void * realloc (void * ptr,size_t size) ;void * alloca (size_t size) ;void free (void * ptr) ;
下面是关于环境变量的函数
1 2 3 4 5 6 7 8 #include <stdlib.h> char * getenv (const char * name) ;int putenv (char * string ) ;int setenv (const char * name,const char * value,int replace) ;int unsetenv (const char * name) ;int clearenv (void ) ;
下面是关于不同函数之间的跳转函数
1 2 3 4 #include <setjump.h> int setjmp (jmp_buf env) ;void longjmp (jmp_buf env,int val) ;
下面是获取进程的资源限制的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <sys/recource.h> int getrlimit (int resource,struct rlimit* rlim) ;int setrlimit (int resource,const struct rlimit* rlim) ;struct rlimit { rlim_t rlim_cur; rlim_t rlim_max; }
下面是获取系统中的限制,**注意不同的标准会有不同的限制名称(name)**,但是使用的函数都是一样的。
1 2 3 4 5 6 7 #include <unistd.h> long sysconf (int name) long pathconf (const char * path,int name) log fpathconf (int fd,int name)
上面后两个函数主要是用来获取路径和文件的限制的,而第一个函数主要是通用的限制
进程属性 这里主要是对于获取进程中的属性的函数:
1 2 3 4 5 6 7 8 #include <sys/time.h> struct tms { clock_t tms_utime; clock_t tms_stime; clock_t tms_cutime; clock_t tms_cstime; }
1 2 3 4 5 6 7 8 #include <sys/times.h> clock_t times (struct tms* buf) struct tms tms_buf;clock_t now = times(&tms_buf);int time_s = now / sysconf(_SC_CLK_TCK);
需要注意,times函数返回的是墙上时间,这个时间的单位是 ticks 而不是秒,所以我们需要将其除以 sysconf(_SC_CLK_TCK) 来得到秒。
下面主要是获取进程的资源使用情况的函数:进程会计 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <sys/acct.h> struct acct { char ac_flag; uint16_t ac_uid; uint16_t ac_gid; uint16_t ac_tty; uint32_t ac_btime; comp_t ac_utime; comp_t ac_stime; comp_t ac_etime; comp_t ac_mem; comp_t ac_io; comp_t ac_rw; comp_t ac_minflt; comp_t ac_majflt; comp_t ac_swaps; uint32_t ac_exitcode; char ac_comm[ACCT_COMM+1 ]; char ac_pad[10 ]; };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <sys/acct.h> int acct (const char * filename) int main () { if (acct("/var/log/process_accounting" )) { perror("acct" ); return 1 ; } printf ("Doing some work...\n" ); sleep(5 ); if (acct(NULL )) { perror("acct" ); return 1 ; } return 0 ; }
会计记录对应于进程而不是程序。在 fork 之后,内核为子进程初始化一个记录,而不是在一个新程序被执行时初始化。如果一个进程顺序执行了 3 个程序(AexecB、BexecC、最后是Cexit),则只会写一个会计记录,并且该记录中的命令名对应于程序 C,但是 CPU 时间是 A、B、C进程之间之和
进程调度 主要是函数设置“友好度”,也就是优先级:
1 2 3 4 5 6 7 8 #include <unistd.h> int nice (int inc) int getpriority (int which,int who) int setpriority (int which,int who,int prio)
第14章 文件系统及其挂载 1.
Linux的系统调度 Linux提供的调度器包括:完全公平调度器(CFS)、实时调度器(RT)、截止时间调度器(DL)
用户名、主机名相关函数
这是获取用户名的方法
1 2 3 4 5 uid_t userid;struct passwd * pwd ; userid = getuid(); pwd = getpwuid(userid);printf ("%s" ,pwd->pw_name);
获取主机名的方法
1 int gethostname (char * buf,size_t size)
进程相关函数
退出函数:1 2 3 4 5 int exit (int status) void _exit (int status) int at_exit (void (*fun)(void )) int on_exit ()
下面是获取进程相关信息的函数:
首先我们需要知道实际、有效之间的关系:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <unistd.h> pid_t getpid (void ) pid_t getppid (void ) uid_t getuid (void ) uid_t geteuid (void ) gid_t getgid (void ) gid_t getegid (void ) uid_t getgroups (int size,gid_t list []) int setuid (uid_t uid) int setgid (gid_t gid) int seteuid (uid_t euid) int setegid (gid_t egid)
需要注意上面这些函数都没有失败返回
下面是进程等待函数(处理函数);
1 2 3 4 5 6 7 8 9 10 11 12 #include <sys/wait.h> pid_t wait (int * status) pid_t waitpid (pid_t pid,int * status,int options) int waitid (idtype_t idtype,id_t id,siginfo_t * infop,int options) int wait3 (int * status,int options,struct rusage* rusage) int wait4 (pid_t pid,int * status,int options,struct rusage* rusage)
错误处理函数
获取gnulibc处理函数:1 const char * gnu_get_libc_version (void ) ;
三个错误处理函数:1 2 3 void perror (const char * str) char * strerr (int error)
移植性问题
关于宏的定义:1 2 3 4 5 6 7 8 _POSIX_SOURCE //一经定义(任何值),头文件会显露符合 POSIX.1-1990 和 ISO C(1990)标准的定义。该宏已被_POSIX_C_SOURCE 取代。 _POSIX_C_SOURCE // _XOPEN_SOURCE // _BSD_SOURCE // _SVID_SOURCE //定义该宏会符合system V的定义 _GNU_SOURCE //该宏一旦定义则符合上面所有
通用文件IO函数 访问模式:flag 以O_开头 访问权限:mode 以S_开头
基本文件操作函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 几个在文件函数中常用的参数: O_RDONLY O_WRONLY O_RDWR O_CREAT O_EXCL O_ARRENDint open (const char * pathname,int flag,...) int openat (int fd, ) int creat (const char * pathname,int mode) int read (int fd,void * buffer,size_t count) int write (int fd,void * buffer,size_t mode) int close (intfd) off_t lseek (int fd,off_t off,iny whereis)
扩展文件操作函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ssize_t pread (int fd,char *buf,size_t count,off_t offest) ssize_t pwrite (int fd,char *buf,size_t count,off_t offest) ssize_t readv (int fd,const struct * iov ,int iovcnt) ssize_t writev (int fd,const struct * iov,int ioncnt) readv () 和writev () 两个函数需要配合struct iovec{void * iov_base;size_t iov_len}第一个是需要读取数据的首地址,第二个是读取的长度 以上二者函数的功能相结合: <sys/uio.h> preadv() pwritev()
大文件(2GB以上)操作:
1 2 3 4 #define _FILE_OOFEST_BITS 64 off64_t open64 () lseek64 ()
创建临时文件:
1 2 int mkstemp (char *tempfile) FILE* tmpfile (void )
链接函数:
1 2 3 4 int link (char * oldfilename,char *newfilename) int unlink (char * pathnae) int symlink (const char * filepath,const char * linkpath) int readlink (const char * filepath,const char * linkpath,int bufsize)
文件/夹相关:
1 2 3 4 5 6 7 8 9 10 11 12 13 int rename (const char * oldfilepath,const char * newfilepath) int remove (const char * filepath) int mkdir (const char * filepath,mode_t mode) int rmdir (const char * filepath,mode_t mode) DIR* opendir (const char * dirpath) void rewindir (DIR* dirname) int closedir (DIR* dirname) int nftw (const char * filepath,int (*func)(const char * pathfile,const struct stat* statbuf,int tepyflage,struct FTW*futbuf),int nopenfd,int flags) char * getcwd (char *buf,size_t size) int chdir (const char * pathfile) int fchdir (int fd)
文件夹相关:
通用IO模型之外的函数 1 2 3 int ioctl (int fd,int request,...) int fcntl (int fd,int cmd,...)
复制文件描述符 实际上就是shell中的重定向操作
1 2 3 int dup (int oldfd) int dup2 (int oldfd,int newfd) int dup3 (int oldfd,int newfd,int flg)
上面三个函数,成功返回新的文件描述符,失败返回-1
高级IO IO多路转接 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <sys/signal.h> int FD_ISSET (int fd, fd_set *fdset) void FD_CLR (int fd, fd_set *fdset) void FD_SET (int fd, fd_set *fdset) void FD_ZERO (fd_set *fdset) fd_set rset;int fd; FD_ZERO(&rset); FD_SET(fd, &rset); FD_SET(STDIN_FILENO, &rset);int select (int maxfdpl, fd_set* readfds,fd_set* writefds,fd_set* exceptfds,struct timeval* timeout) int pselect (int nfds,fd_set* readfds,fd_set* writefds,fd_set* exceptfds,const struct timespec* timeout,const sigset_t * sigmask) int poll (struct pollfd* fds,nfds_t nfds,int timeout) int epoll_create (int size)
信号
信号的发送
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 kill(1234 ,9 ) raise(int ) alarm(time)#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main (int argc, const char *argv[]) { printf ("%d\n" ,alarm(7 )); sleep(2 ); while (1 ) { printf ("%d\n" ,alarm(3 )); sleep(7 ); } return 0 ; }
信号的接收
1 int pause (void ) 该函数会中断进程,然后等待一个信号的到来,在执行该信号。该函数只会返回-1
信号的处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 signal(SIG_INT,SIG_IGN) SIG_IGN无视 SIG_DFL默认 第三种选择就是使用指定的函数来处理信号。#include <signal.h> void (*signal(int signum, void (*handler)(int )))(int ); 参数 signum:一个整数,表示信号的编号。 handler:一个指向信号处理函数的指针,或者一个特殊的值 SIG_IGN(忽略信号)或 SIG_DFL(使用默认处理程序)。 返回值 如果 signal() 调用成功,它将返回先前注册的信号处理函数的指针。 如果调用失败,返回值将是 SIG_ERR。
上面这个函数的局限性在于,不改变信号的处理方式就无法知道信号的现在的处理方式。所以,如果想知道信号的现在的处理方式,需要使用sigaction函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <signal.h> int sigaction (int signum, const struct sigaction* act, struct sigaction* oldact) ;struct sigaction { void (*sa_handler)(int ); sigset_t sa_mask; int sa_flags; void (*sa_sigaction)(int , siginfo_t *info, void *); };struct siginfo { int si_signo; int si_errno; int si_code; pid_t si_pid; uid_t si_uid; int si_status; void *si_addr; union sigval si_value ; };union sigval { int sival_int; void *sival_ptr; };int sigprocmask (int how,const sigset_t * set ,sigset_t * oldset) int sigpending (sigset_t * set ) int sigsuspend (const sigset_t * mask) int sigwait (const sigset_t * set ,int * sig) int sigwaitinfo (const sigset_t * set ,siginfo_t * info)
信号分为标准信号和实时信号。信号产生的原因:1. 硬件产生的异常,如/0,访问了不可以访问的内存等等;2. 发生了软件定义的事件;3. 用户按下了具有信号作用的终端特殊字符·
对于信号集操作的函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <signal.h> int sigemptyset (sigset_t * set ) int sigfillset (sigset_t * set ) int sigaddset (sigset_t * set ,int signum) int sigdelset (sigset_t * set ,int signum) int sigismember (const sigset_t * set ,int signum) int sigprocmask (int how, const sigset_t * restrict set , sigset_t * restrict oldset) int sigpending (sigset_t * set ) int sigsuspend (const sigset_t * mask)
信号中函数跳转 这两个函数的使用常见主要是在信号处理函数中,用于跳转到其他函数中,主要是 main()
1 2 3 4 5 #include <signal.h> int sigsetjmp (sigjmp_buf env,int savemask) void siglongjmp (sigjmp_buf env,int val)
信号的发送 1 2 3 4 5 6 7 8 9 10 11 12 #include <signal.h> int kill (pid_t pid,int sig) int reise (int signo) #include <stdlib.h> void abort (void )
上面两个函数成功都返回 0 ,失败都是 -1
信号名和编码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <signal.h> void psignal (int sig, const char * message) psignal (SIGINT,"Signal SIGINT is" ) ;void psiginfo (const siginfo_t * info, const char * message) int sig2str (int signo, char *str) int str2sig (const char *str, int *signop) #include <string.h> char *strsignal (int signo)
共享内存
创建共享内存:1 2 3 int shmget (key_t key,size_t size,int shmflg) ftok (const char * pathname,int proj_id)
映射共享内存地址至用户空间1 2 3 4 5 void * shmat (int shmid,const void *shmaddr,int shmfig) int shmdt (const void shmaddr) int shmctl (const void shmaddr)
消息队列
创建消息队列1 int msgget (key_t key,int msgflg)
删除消息队列1 int msgctl (int msgid,int cmd,struct msqid_ds* buf) ;
杂项
无名管道:适用于有亲缘关系的进程,使用函数pipe()创建:
1 2 3 4 5 6 7 8 9 int fd[2 ]; pipe(fd); write(fd[1 ],"nihao" ,5 );char buffer[100 ]; read(fd[0 ],buffer,32 );
有名管道:可以使用于没有关系的进程。使用函数mkfifo()函数创建,注意该函数只是创建一个文件,这相当于无名管道中的函数pipe()所创建的文件。有名管道使用该文件进行没有关系之间的进程的交流。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int ret; ret = mkfifo("filename" ,umake)if (ret == 0 ){ perror("mkfifo funtion error" ); exit (1 ); }if (access("filename" ,F_OK) == -1 ){ fd = open("filename" ,O_WRONLY); if (fd == -1 ){ perror("open funtion error " ); exit (1 ); } } write(fd,"nihao" ,5 );
注意不管是无名管道还是又名管道,在读取没有内容的文件时都会阻塞。
选项处理函数getopt()1 2 3 4 5 6 7 8 9 int getopt (int agrc,char *const *agrv,char * optstring) 返回值是:到底了返回-1,出问题了也是-1 这个函数还包括四个不在函数参数列表中的变量: 1. extern int opterr:这个参数表示设置为1打印错误消息,设置为0不打印错误消息 2. extern int optind:这个表示指向下一个argv中未处理的选项 3. extern int optopt:这个哪一个选项出了问题 4. extern char * optarg:这个是表示选项所带的参数
中断函数可以传参吗 中断处理函数不能有返回值和形式参数,因为中断处理函数是由硬件(或触发器)调用的,没有程序向其传递参数,也没有程序接收其返回值,其参数通过全局变量传递。
但是,请注意,如果要检测的其他函数的全局变量值在中断服务函数中发生更改,则volatile关键字将用于定义全局变量。因为主程序可能会将变量读入寄存器,然后每次只使用寄存器中的变量副本。如果此时不使用volatile关键字,则在中断服务函数中修改变量的操作将被短路。
线程 线程的基本操作函数:创建、删除、标识 在线程中有三种方式可以退出线程:
线程函数返回,return语句
调用pthread_exit()函数,退出函数
调用pthread_cancel()函数,该函数可以请求取消同一个进程中的其他线程。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <pthread.h> int pthread_create (pthread_t * thread,const pthread_attr_t * attr,void *(*start_routine)(void *),void * arg) int pthread_exit (void *retval) int pthread_join (pthread_t thread,void **retval) pthread_t pthread_self (void ) int pthread_equal (pthread_t t1,pthread_t t2) int pthread_cancel (pthread_t thread) int pthread_detach (pthread_t thread) void pthread_cleanup_push (void (*rtn)(void *),void * arg) void pthread_cleanup_pop (int execute)
使用例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #include <stdio.h> #include <sys/types.h> #include <pthread.h> #include <unistd.h> pthread_t ntid;void * test_pthread (void * t) { pthread_t tid = pthread_self(); printf ("son thread tid is :%ld\n" ,tid); printf ("son pid is :%d\n" ,getpid()); return (void *) 0 ; }void print_pid_tid (void ) { printf ("father pid is :%d\n" ,getpid()); printf ("father tid is :%ld\n" ,pthread_self()); printf ("\n\n" ); }int main (void ) { pthread_create(&ntid,NULL ,test_pthread,NULL ); print_pid_tid(); sleep(2 ); return 0 ; }
线程的同步函数:互斥锁、条件变量、读写锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <pthread.h> #include <time.h> int pthread_mutex_init (pthread_mutex_t *restrict mutex,const pthread_mutexattr_t * restrict attr) int pthread_mutex_destroy (pthread_mutex_t * mutex) int pthread_mutex_lock (pthread_mutex_t * mutex) int pthread_mutex_unlock (pthread_mutex_t * mutex) int pthread_mutex_trylock (pthread_mutex_t * mutex) int pthread_mutex_timedlock (pthread_mutex_t * mutex,const struct timespec* restrict tsptr) int pthread_cond_init (pthread_cond_t *restrict cond,const pthread_condattr_t * restrict attr) int pthread_cond_destroy (pthread_cond_t *restrict cond) int pthread_cond_wait (pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex) int pthread_cond_signal (pthread_cond_t *restrict cond) int pthread_cond_broadcast (pthread_cond_t *restrict cond) int pthread_cond_timedwait (pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec* restrict tsptr) int pthread_rwlock_init (pthread_rwlock_t * rwlock,const pthread_rwlockattr_t *restrict attr) int pthread_rwlock_destroy (pthread_rwlock_t * rwlock) int pthread_rwlock_rdlock (pthread_rwlock_t * rwlock) int pthread_rwlock_wrlock (pthread_rwlock_t * rwlock) int pthread_rwlock_unlock (pthread_rwlock_t * rwlock) int pthread_rwlock_tryrdlock (pthread_rwlock_t * rwlock) int pthread_rwlock_trywrlock (pthread_rwlock_t * rwlock) int pthread_rwlock_timedrdlock (pthread_rwlock_t * rwlock,const struct timespec* restrict tsptr) int pthread_rwlock_timedwrlock (pthread_rwlock_t * rwlock,const struct timespec* restrict tsptr)
上面所有的函数的返回值都是成功则返回 0 ,失败则返回错误编码
线程控制 主要是线程、同步方式的属性控制函数
线程控制函数: 结构体 pthread_attr_t 具有四个参数,分别是:
detachstate:线程分离状态,默认值为 PTHREAD_CREATE_JOINABLE,表示线程是可分离的。
guardsize:线程栈末尾的警戒缓冲区的大小(以字节为单位)
stackaddr:线程栈的最低地址
stacksize:线程栈的最小长度(以字节为单位)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <pthread.h> int pthread_attr_init (pthread_attr_t * attr) int pthread_attr_destroy (pthread_attr_t * attr) int pthread_attr_setdetachstate (pthread_attr_t * attr,int detachstate) int pthread_attr_getdetachstate (const pthread_attr_t * attr,int * detachstate) int pthread_attr_getstack (const pthread_attr_t * attr,void ** stackaddr,size_t * stacksize) int pthread_attr_setstack (pthread_attr_t * attr,void * stackaddr,size_t stacksize) int pthread_attr_getstacksize (const pthread_attr_t * attr,size_t * stacksize)
上面这几个函数的返回值都是成功则返回值,失败则返回 -1 并设置 errno == EINVAL。同时,有些 name 会返回一个变量名(>= 0)或者是提示该值是不确定的。不确定的至通过返回 -1 但是不设置 error 来表示
线程同步方式的属性 我们知道线程同步的方式包括下了:
互斥锁
条件变量
读写锁
屏障
信号 下面是这些同步方式的属性控制函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 #include <pthread.h> int pthread_mutexattr_init (pthread_mutexattr_t *restrict attr) int pthread_mutexattr_destroy (pthread_mutexattr_t *restrict attr) int pthread_mutexattr_setpshared (pthread_mutexattr_t *restrict attr,int pshared) int pthread_mutexattr_getpshared (const pthread_mutexattr_t *restrict attr,int * pshared) int pthread_mutexattr_settype (pthread_mutexattr_t *restrict attr,int type) int pthread_mutexattr_gettype (const pthread_mutexattr_t *restrict attr,int * type) int pthread_mutex_consistent (pthread_mutex_t *restrict mutex) ## 时间函数 主要是获取当前时间和时间相关的函数。 在这之前我们需要先明白下面这张图: ![](https: 从图上可以看出,在 Linux 中主要有两种时间: 1. 日历时间2. 分解时间time_t 是一个非负数的整数,用以表示从 1970 年 1 月 1 日 00 :00 :00 UTC 到现在所经过的秒数。struct tm 是一个结构体,用来表示分解时间。```c #include <time.h> struct tm { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_year; int tm_wday; int tm_yday; int tm_isdst; }; 我们还可以从图上看出,`timeval` `time_t ` `timespec` 其实表示的是同一个东西,不过精准度不同。`timeval` 精确到纳秒,`time_t ` 精确到秒,`timespec` 精确到微秒。所以这里面精确度最高的是 `timespec`。但是不常用。 其中还有两个函数需要注意:`ctime()` 和 `asctime()`。这两个函数可以直接将 `time_t ` 转换为字符串,`asctime()` 还可以将 `struct tm` 转换为字符串。 ```c #include <time.h> struct timespec { time_t tv_sec; long tv_nsec; }; #include <sys/time.h> struct timeval { time_t tv_sec; suseconds_t tv_usec; }; #inlcude <time.h> clockid_t 这是一个算数类型,同时内核为制定了一些选项:CLOCK_REALTIME: 真实时间时钟,表示从某个参考时间点(通常是系统启动)开始的流逝时间。 CLOCK_MONOTONIC: 单调递增时钟,表示从某个参考时间点开始的流逝时间,不受系统时间调整的影响。 CLOCK_PROCESS_CPUTIME_ID: 当前线程的 CPU 时间。 CLOCK_THREAD_CPUTIME_ID: 当前线程的 CPU 时间。 CLOCK_MONOTONIC_RAW: 与 CLOCK_MONOTONIC 类似,但不受 NTP 调整的影响。 CLOCK_BOOTTIME: 自系统启动以来的时间,但不包括挂起时间。 CLOCK_SGI_CYCLE: SGI 系统上的周期计数器。 CLOCK_TAI: 国际原子时(TAI)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 #include <time.h> #include <sys/time.h> time_t time (time_t * t) time_t now = time(NULL );char * ctime (const time_t * timep) char * now = ctime(&now);char * asctime (const struct tm* timeptr) char * now = asctime(localtime(&now));struct tm* localtime (const time_t * timep) struct tm* now = localtime(&now);struct tm* gmtime (const time_t * timep) struct tm* utcnow = gmtime(&now);int gettimeofday (struct timeval* tv,void *restrict tz) struct timeval now; gettimeofday(&now, NULL );int settimeofday (const struct timeval* tv,const struct timezone* tz) struct timeval now; gettimeofday(&now, NULL ); struct timeval new_time ; new_time.tv_sec = 0 ; new_time.tv_usec = 1000000 ; settimeofday(&new_time, NULL );int clock_gettime (clockid_t clk_id,struct timespec* tp) struct timespec now; timespec.tv_sec = 0 ; timespec.tv_nsec = 1000000 ; clock_gettime(CLOCK_REALTIME, &now);int clock_settime (clockid_t clk_id,const struct timespec* tp) struct timespec new_time; new_time.tv_sec = 0 ; new_time.tv_nsec = 1000000 ; clock_settime(CLOCK_REALTIME, &new_time);int clock_getres (clockid_t clk_id,struct timespec* res) struct timespec res; clock_getres(CLOCK_REALTIME, &res); printf ("The minimum resolution is: %ld.%09ld\n" , res.tv_sec, res.tv_nsec);size_t strftime (char *restrict s,size_t maxsize,const char *restrict format,const struct tm*restrict timeptr) char time_str[64]; struct tm * now = localtime(&now); strftime(time_str, 64 , "%Y-%m-%d %H:%M:%S" , now); printf ("The time is: %s\n" , time_str);size_t strftime_l (char *restrict s,size_t maxsize,const char *restrict format,const struct tm*restrict timeptr,locale_t locale) char time_str[64]; struct tm * now = localtime(&now); locale_t mylocale = newlocale(LC_TIME_MASK, "zh_CN.UTF-8" , NULL ); strftime_l(time_str, 64 , "%Y-%m-%d %H:%M:%S" , now, mylocale); printf ("The time is: %s\n" , time_str); char * strptime (const char * restrict s,const char * restrict format,struct tm*restrict tm) char time_str[] = "2021-08-28 10:00:00" ; struct tm time_tm ; strptime(time_str, "%Y-%m-%d %H:%M:%S" , &time_tm); printf ("The year is: %d\n" , time_tm.tm_year); printf ("The month is: %d\n" , time_tm.tm_mon);
SUSv3 规定,调用 ctime()、gmtime()、localTime()或 asctime()中的任一函数,都可能会覆盖由其他函数返回,且经静态分配的数据结构。换言之,这些函数可以共享返回的字符数组和 tm 结构体,某些版本的 glibc 也正是这样实现的。如果有意在对这些函数的多次调用间维护返回的信息,那么必须将其保存在本地副本中。
time类函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <unistd.h> unsigned int sleep (unsigned int seconds) int nanosleep (const struct timespec* req,struct timespec* rem) int usleep (useconds_t useconds) usleep (1000000 ) ;int clock_nanosleep (clockid_t clk_id,int flags,const struct timespec* req,struct timespec* rem) struct timespec req = {10 , 0 }; clock_nanosleep(CLOCK_REALTIME, 0 , &req, NULL ); ## 系统标识 ```c#include <sys/utsname.h> struct utsname { char sysname[]; char nodename[]; char release[]; char version[]; char machine[]; };int uname (struct utsname* buf)
1 2 3 4 5 #include <unistd.h> int gethostname (char * name,size_t len)
网络Socket socket() 函数用于创建套接字,并返回一个文件描述符。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> int socketid = socket(family, type, protocol); socketid: socket 描述符,可以看做是一个文件描述符,通过它来读/写数据 family:整数,通信域。 AF_INET:因特网协议协议,网络地址,最常用。 AF_UNIX,本地通信,文件地址 type:通信类型 SOCK_STREAM:可靠的,面向连接的服务,TCP 协议 SOCK_DGRAM:不可靠,无连接的服务,UDP 协议 SOCK_RAW:需要自己管理 IP 头部的数据 protocol:协议 一般设为 0 , 表示使用默认协议 IPPROTO_TCP,IPPROTO_UDP 如果出错的话,socketid 返回值是 -1 。
绑定三元组到 socket
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int bind (int fd, const struct sockaddr *, socklen_t ) ; fd: socket 描述符 addr: 通信地址,一般是 IP 地址和端口号的组合 addrlen: 地址长度struct sockaddr_in { short sin_family; u_short sin_port; struct in_addr sin_addr ; char sin_zero[8 ]; };struct in_addr { uint32_t s_addr; };
监听 socket
1 2 3 4 5 6 7 int listen (int fd, int backlog) ; fd: socket 描述符 backlog: 第二个参数是最大连接数,表示发来请求但是没有被 accept 的连接数量。 listen 函数在成功时返回 0 ,失败时返回 -1 ,并且设置错误代码。
接受连接
1 2 3 4 5 int accept (int fd, struct sockaddr *addr, socklen_t *addrlen) ; fd: socket 描述符 addr: 通信地址,一般是 IP 地址和端口号的组合 addrlen: 地址长度
请求连接 socket
1 2 3 4 5 int connect (int fd, const struct sockaddr *addr, socklen_t addrlen) ; fd: socket 描述符 addr: 通信地址,一般是 IP 地址和端口号的组合 addrlen: 地址长度
发送数据和接受数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 ssize_t send (int fd, const void *buf, size_t len, int flags) ; fd: socket 描述符 buf: 发送数据缓冲区 len: 发送数据长度 flags: 发送标志 MSG_OOB:发送带外数据 sendto(int sockfd, const void *msg, int len, unsigned int flags,const struct sockaddr *to, socklen_t tolen); sockfd: socket 描述符 msg: 发送数据缓冲区 len: 发送数据长度 flags: 发送标志 to: 目标地址 tolen: 目标地址长度 如同 send(),sendto() 会返回实际已传送的资料数量(一样,可能会少於你要传送的资料量!)而错误时返回 -1 。int recv (int sockfd, void *buf, int len, int flags) ; sockfd: socket 描述符 buf: 接收数据缓冲区 len: 接收数据长度 flags: 接收标志 recv() 返回实际读到并写入到缓冲区的 byte 数目,而错误时返回 -1 [并设置相对的 errno]。 recv() 会返回 0 ,这只能表示一件事情:远端那边已经关闭了你的连接!recv() 返回 0 的值是让你知道这件事情。int recvfrom (int sockfd, void *buf, int len, unsigned int flags, struct sockaddr *from, int *fromlen) ; 一样,它跟 recv() 很像,只是多了两个栏位。from 是指向 local struct sockaddr_storage 的指针,这个数据结构包含了数据包来源的 IP address 与 port 。fromlen 是指向 local int 的指针,应该要初始化为 sizeof *from 或是 sizeof (struct sockaddr_storage )。当函数返回时,fromlen 会包含实际上储存於 from 中的地址长度。recvfrom () 返回接收的数据数目,或在发生错误时返回 -1[并设置相对的 errno ]。
关闭 socket
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 int close (int fd) ; fd: socket 描述符int shutdown (int sockfd, int how) ; 其他常用函数 ```c#include <netdb.h> struct hostent * gethostbyname (const char *name) ; 参数 name 是诸如 www.google.com 的字符串,返回值是 struct hostent 结构体,用来存储得到的地址信息。struct hostent { char *h_name; char **h_aliases; int h_addrtype; int h_length; char **h_addr_list; }; #include <arpa/inet.h> char *inet_ntoa (struct in_addr) ; int inet_aton (const char *cp, struct in_addr *inp) ; #include <arpa/inet.h> in_addr_t inet_addr (const char *ip) ;#include <sys/socket.h> int getpeername (int sockfd, struct sockaddr *addr, int *addrlen) ; sockfd: socket 描述符 addr: 通信地址,一般是 IP 地址和端口号的组合 addrlen: 地址长度 函数在错误时返回 -1 ,并设置相对的 errno。#include <unistd.h> int gethostname (char *hostname, size_t size) ; hostname: 保存主机名的缓冲区 size: 主机名的长度 函数在运行成功时返回 0 ,在错误时返回 -1 ,并一样设置 errno。