unix 高级环境编程系列2

此文档主要整理并记录liunx中常用的api.

IO

lseek

1
2
#include<unistd.h>
off_t lseek(int fd, off_t offset, int whence);
  • 功能:显式地为一个打开文件设置偏移量。
  • 返回值:若函数成功执行,返回新的文件偏移量。
    lseek函数只修改文件表项中的当前文件偏移量,不进行任何I/O操作。
  • 参数whence
    • 当whence为SEEK_SET,则将该文件的偏移量设置为距文件开始处offset个字节。
    • 当whence为SEEK_CUR, 将该文件的文件偏移量设置为其当前值加offset;
    • 当whence为SEEK_END,则该文件的文件偏移量设置为文件长度加offset。

dup和dup2

1
2
3
#include<unistd.h>
int dup(int fd);
int dup2(int fd, int fd2);

dup和dup2都可以用来复制一个现有的文件描述符。由dup返回的新文件描述符一定是当前可用文件描述符中的最小数值。对于dup2,可以用fd2参数指定新描述符的值。如果fd2已经打开,则先将其关闭。如果fd等于fd2,则dup2返回fd2,而不关闭它。
返回值:成功时,返回当前可用的最小数值的文件描述符。
这些函数返回的文件描述符与参数fd共享同一个稳健表项。

fcntl

1
2
#include<fcntl.h>
int fcntl(int fd, int cmd, ... /* int arg*/);
  • fcntl函数有多种功能
    • 复制文件描述符(cmd = F_DUPFD或者F_DUPFD_CLOEXEC)
    • 获取/设置文件描述符标志(cmd = F_GETFD或者F_SETFD)
    • 获取/设置文件状态标志(cmd = F_GETFL或者F_SETFL)

文件状态标志和当前文件偏移量都在文件表项中

  • 复制一个已有的文件描述符

    int ret = fcntl(fd, F_DUPFD);
    等价于int dup(fd);

  • 获取/设置文件状态标志

    open的flags参数

    • 获取文件的状态标识

      flag = flag | O_APPEND;

    • 设置文件的状态标识

      fcntl(fd, F_SETFL, flag);

    • 可以更改的几个标识:O_APPEND,O_NONBLOCK

open

1
2
3
4
#include<fcntl.h>

int open(const char *path, int oflag, .../* mode_t mode */);
int openat(int fd, const char* path, int oflag, .../* mode_t mode*/);

返回值:若成功,返回文件描述符;若出错,返回-1。

read

函数原型

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

成功时返回接收的字节数(但遇到文件结尾则返回0),失败时返回-1.
fd: 显示数据接收对象的文件描述符.
buf: 要保存接收数据的缓冲地址值.
nbytes: 要接收数据的最大字节数.

write

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

成功时返回写入的字节数,失败时返回-1.
fd: 显示数据传输对象的文件描述符.
buf: 要保存传输数据的缓冲地址值.
nbytes: 要传输数据的最大字节数.

read和write都是系统调用。read和write都在内核执行,所以称这些函数为不带缓冲的IO函数。

fgetc

fgetc从文件指针stream指向的文件中读取一个字符,读取一个字节后,光标位置后移一个字节。

fputc

功能:把一个字符写到一个输出文件流中.它返回写入的值,如果失败,则返回EOF.
putc函数的作用相当于fputc,但它可能被实现为一个宏.
putchar相当于putc(c, stdout),但它把单个字符写到标准输出.注意:putchar和getchar都是把字符当做int类型而不是char类型来使用的.这就允许文件尾标识取值-1,这是一个超出字符数字编码范围的值.

fopen

https://blog.csdn.net/hairetz/article/details/4150193

fread

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)

  • 功能:从给定流 stream 读取数据到 ptr 所指向的数组中
  • ptr – 这是指向带有最小尺寸 size\nmemb* 字节的内存块的指针。
  • size – 这是要读取的每个元素的大小,以字节为单位。
  • nmemb – 这是元素的个数,每个元素的大小为 size 字节。
  • stream – 这是指向 FILE 对象的指针,该 FILE 对象指定了一个输入流。

fgets

函数原型为:char *fgets(char *str, int n, FILE *stream)

  • 功能:从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。fgets会把读到字符写到s指向的字符串里,直到出现下面某种情况:

    • 遇到换行符
    • 已经传输了n-1个字节
    • 到达文件尾

    它会把遇到的换行符也保存到s中.最后在s末尾加上一个表示结尾的空字符\0.一次调用最多只能传输n-1个字符,因为它必须把空字符加到字符串表示结尾.

  • gets函数类似于fgets,但是它从标准输入流中取数据并丢弃遇到的换行符.它在接收字符串的尾部加上一个空字节(一个字符占一个字节).

fflush

函数原型

1
int fflush(FILE *stream);

功能:把文件流里的所有未写数据立刻写出(将进程缓冲区中的数据送入内核缓冲区中还是将数据从内核缓冲区写入磁盘?)。可以用这个函数来确保试图读入一个用户响应之前,先向终端送出一个交互提示符。使用这个函数还可以确保在程序继续执行之前重要的数据都已经被写到磁盘上。有时在调试程序时,你还可以用它来确认程序是正在写数据而不是被挂起了。注意,调用fclose函数隐含执行了一次flush操作,所以不必在调用fclose之前调用fflush。

puts

1
2
# include <stdio.h>
int puts(const char *s);

功能:将字符串输出到屏幕。输出时只有遇到 ‘\0’ 也就是字符串结束标志符才会停止。

pread

功能:对文件随机读。

pwrite

功能:对文件随机写。

文件和目录

opendir

1
2
3
#include <sys/types.h>   
#include <dirent.h>
DIR * opendir(const char * name);
  • 功能:opendir()用来打开参数name指定的目录,并返回DIR*形态的目录流.接下来对目录的读取和搜索都要使用此返回值.
  • 返回值:成功则返回DIR*型态的目录流,打开失败则返回nullptr.
  • 错误代码:
    • EACCESS 权限不足
    • EMFILE 已达到进程可同时打开的文件数上限
    • ENFILE 已达到系统可同时打开的文件数上限
    • ENOTDIR 参数name 非真正的目录
    • ENOENT 参数name 指定的目录不存在, 或是参数name 为一空字符串
    • ENOMEM 核心内存不足

stat

1
2
#include<sys/stat.h>
int stat(const char* restrict pathname, struct stat *restrict buf);

功能:给定pathname,stat函数返回与此命名文件有关的信息结构。

lstat

1
2
#include<sys/stat.h>
int lstat(const char*restrict pathname, struct stat *restrict buf);

lstat函数类似于stat,但是当命名文件是一个符号链接时,lstat返回该符号链接的有关信息,而不是由该符号链接引用的文件信息。

进程

fork

1
2


vfork

函数原型

1
2


  • 功能:vfork函数用于创建一个新进程.新进程的目的是exec一个新程序.
    • vfork和fork一样都创建一个子进程,但是它并不将父进程的地址空间完全复制到子进程中.因为子进程会立即调用exec(exit),于是也就不会引用该地址空间.但是在子进程调用exec或exit之前,它在父进程的空间中运行.
    • vfork和fork之间的另一个区别是:vfork保证子进程先运行.在它调用exec或exit之后父进程才可能被调度运行.当子进程调用这两个函数中的任意一个时,父进程会恢复运行.如果在调用这两个函数之前子进程依赖于父进程的进一步动作,会导致死锁.

wait

在父进程中通过调用wait函数来让父进程等待子进程的结束.wait系统调用将阻塞父进程直到它的子进程结束.这个调用返回子进程的PID,它通常是已经结束运行的子进程的PID.状态信息允许父进程了解子进程的退出状态,即子进程的main函数返回的值或子进程中exit函数的退出码.如果stat_loc不是空指针,那么状态信息就会被写入它所指向的位置.
如果调用wait的进程有多个子进程,那么在其某一子进程终止时,wait就立即返回.

1
2
#include<sys/wait.h>
pid_t wait(int *stat_loc)

头文件sys/wait.h中定义的宏来解释状态信息.

1
2
WIFEXITED(stat_val);		// 如果子进程正常结束,它就取一个非零值
WEXITSTATUS(stat_val); // 如果WIFEXITED非零,它返回子进程的退出码

waitpid

函数原型

1
2
3
#include<sys/type.h>
#include<sys/wait.h>
pid_t waitpid(pid_t pid, int *stat_loc, int options)
  • 功能:waitpid用来等待某个特定进程的结束.
  • pid参数用于指定需要等待的子进程的PID.如果它的值为-1,waitpid将返回任一子进程的信息.
  • 如果stat_loc不是空指针,waitpid将状态信息写入到stat_loc指定的位置.
  • option参数可用来改变waitpid的行为,其中一个最有用的选项是WNOHANG,它的作用是防止waitpid调用将调用者的执行挂起.可以使用这个选项来查找是否有子进程已经结束,如果没有,程序将继续执行.其他的选项和wait调用的选项相同.
  • 返回值:如果子进程没有结束或意外终止,返回0;否则返回child_pid.如果waitpid失败,它将返回-1并设置errno.失败的情况包括:没有子进程(errno设置为ECHILD),调用被某个信号中断(EINTR),或选项参数无效(EINVAL).

exec函数族

1
int execl(const char*path, const char *arg,...)

exec函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行
参数
path:可执行文件的路径名字
arg:可执行程序所带的参数,第一个参数为可执行文件的名字,没有带路径且arg必须以空指针结束

https://blog.csdn.net/u014530704/article/details/73848573

进程同步

用于进程的信号量

线程

pthread_create

函数原型

1
int pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void*), void* arg);
  • 功能:创建一个新线程
  • 第一个参数指向一个pthread_t 类型的指针,线程被创建时,这个指针指向的变量中将被写入一个标识符,我们用该标识符来引用新线程.
  • 第二个参数用于设置线程的属性;一般不需要特殊的属性,因此令该参数为NULL
  • 第三个参数一个函数指针,告诉线程将要启动执行的函数
  • 第四个参数是传递给该函数的参数
  • 成功时,返回0;失败时,返回错误代码

pthread_join

函数原型

1
int pthread_join(pthread_t th, void **thread_return);
  • 功能:pthread_join等价于进程中用来收集子进程信息的wait函数.调用进程将一直阻塞,直到指定的线程调用thread_exit或者从启动例程(即被调用的函数)中返回,或被取消。
  • 第一个参数指定将要等待的线程,线程通过pthread_create返回的标识符来指定。第二个参数是一个指向指针的指针,而后者指向线程的返回值。
  • 返回值:成功时返回0,失败时返回错误代码。

pthread_exit

函数原型

1
int pthread_exit(void * rval_ptr);

线程同步

线程同步有几种方式。
信号量、互斥量、

用于线程的信号量

有两组接口函数用于信号量.一组取自POSIX的实时扩展,用于线程.另一组被称为系统V信号量,常用于进程的同步。
信号量是一个特殊类型的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作。 在多线程程序中,如果一个程序中有两个(或多个)线程试图改变一个信号量的值,系统将保证所有的操作都依次进行。信号量有二进制信号量和计数信号量。信号量一般用来保护一段代码,使其每次只能被一个执行线程运行,使用二进制信号量来完成这个工作。允许有限数目的线程执行一段指定的代码,这时需要计数信号量。

sem_init

函数原型

1
int sem_init(sem_t *sem, int pshared, unsigned int value);
  • 功能:创建信号量;信号量由一个sem_t类型指针指定,设置该信号量对象的共享选项,并给它一个初始的整数值.
  • 第一个参数为指向sem_t类型的信号量对象的指针.
  • pshared表示该信号量对象的共享选项,若为0表示该信号量是当前进程的局部信号量,否则,这个信号量就可以在多个进程之间共享.

sem_wait

函数原型

1
int sem_wait(sem_t* sem);
  • 功能:以原子操作的方式给信号量的值减1.但是它会等待直到信号量有个非零值才会开始减法操作.

sem_post

函数原型

1
int sem_post(sem_t* sem);
  • 功能:以原子操作的方式给信号量的值加1.

互斥量

另一种用在多线程程序中的同步访问方法是使用互斥量。它允许程序锁住某个对象,使得每次只能有一个线程访问它。为了控制对关键代码的访问,必须在进入这段代码之前锁住一个互斥量,然后在完成操作之后解锁它。
互斥量又称为互斥锁,主要用于线程互斥,不能保证按序访问,可以和条件锁一起实现同步。当进入临界区时,需要获得互斥锁并加锁;当离开临界区时,需要对互斥锁解锁,以唤醒其他等待该互斥锁的线程。
以下这些用于互斥量的基本函数成功时返回0,失败时返回错误代码,但是这些函数并不设置errno,必须对函数的返回代码进行检查。

pthread_mutex_init

函数原型

1
int pthread_mutex_init(pthread_mutext_t *mutex, const pthread_mutexattr_t *mutexattr);

功能:初始化互斥锁。

pthread_mutex_lock

函数原型

1
int pthread_mutex_lock(pthread_mutex_t *mutex)

功能:以原子操作的方式给一个互斥锁加锁,如果目标互斥锁已经被上锁,pthread_mutex_lock调用将阻塞,直到该互斥锁的占有者将其解锁。

pthread_mutex_unlock

函数原型

1
int pthread_mutex_unlock(pthread_mutex_t *mutex);

pthread_mutex_destroy

函数原型

1
int pthread_mutex_destroy(pthread_mutex_t *mutex)

功能:以原子操作的方式给一个互斥锁解锁。

条件变量

条件变量又称条件锁,用于在线程之间同步共享数据的值。条件变量提供一种线程间通信机制;当某个共享数据达到某个值时,唤醒等待这个共享数据的一个/多个线程。即,当某个共享变量等于某个值时,调用signal/broadcast。此时操作共享变量需要加锁。其主要系统调用如下:
pthread_cond_init:初始化条件变量
pthread_cond_destory:销毁条件变量
pthread_cond_signal:唤醒一个等待目标条件变量的线程。哪个线程被唤醒取决于调度策略和优先级。
pthread_cond_wait:等待目标条件变量。需要一个加锁的互斥锁确保操作的原子性。该函数中在进入wait状态前首先进行解锁,然后接收到信号后会再加锁,保证该线程对共享资源正常访问。

信号

signal

函数原型

1
2
#inclue<signal.h>
void (*signal(int signo, void (*func)(int)))(int);

void (*func)(int)表示func是一个未初始化的函数指针,func指向的这个函数需要一个int类型的参数,返回void。
上述定义可以写成下面这样:

1
2
void (*func)(int);					// func是一个未初始化的函数指针
void (*signal(int signo, func))(int);

由内往外的顺序阅读第二条声明语句。因为signal有形参列表,所以signal是一个函数,它需要两个参数,一个是int类型参数,表示信号名;另一个是当接收到此信号后要调用的函数的地址func。
signal前面有*,所以signal返回一个指针;指针类型本身也包含形参列表,因此指针指向函数,所以signal函数的返回值是一个函数指针。这个函数函数指针指向的函数有一个int类型参数,返回类型为void。

如果func的值为SIG_IGN,表示向内核忽略此信号(但是SIGKILL和SIGSTOP不能被忽略)
如果func的值为SIG_DFL,表示接到此信号后的动作是系统默认动作。
当指定函数地址时,则在信号发生时,调用该函数,我们称这种处理为捕捉该信号。func指定的函数为信号处理函数或者信号捕捉函数。