目录: 1.普通的IO函数

2.socket函数

3.IO复用函数

4.signal函数

5.aio编程函数

6. POSIX(文件操作)和目录管理

7.原始套接字

8.进程通信IPCs

9.多线程编程

_________________________________________其他函数___________________________

宏(包括函数式宏) 没有返回值的概念,因为它只是在源代码文件(级别)上的文本替换(即修改些源代码),没有一个切换并保护现场的过程,所以不需要也不存在一个通过返回值将其的结果传送回来的过程。还有,宏所代表的表达式的计算结果是叫一个结果值,不叫(函数)返回值。例如,

#define A(a,b,c) ({a=1;b+=1;c=3;a+b+c;})

#include

int main()

{

int a;

int b=1;

int c;

int d;

d=A(a,b,c);

printf("%d,%d,%d,%d\n",a,b,c,d);

return 0;

}

宏定义的概念 https://blog.csdn.net/haiross/article/details/46470533

void *memset(void *s,int c,size_t n) 将已开辟内存空间 s 的首 n 个字节的值设为值 c , 可以清空结构体或数组

void bzero(void *s, int n); 会将参数s 所指的内存区域前n 个字节,全部设为零值。

他们的区别: https://zhidao.baidu.com/question/507145427.html

int strcmp(const char *s1, const char *s2); 两个字符串比较大小 一样返回0 (s1>s2)? >0 :<0

int strncasecmp(char* s1 ,char* s2 ,size_t n) 比较两个字符串前n个字符的大小, 一样返回0 , (s1>s2) ? >0 : <0

int memcmp(const void *str1, const void *str2, size_t n)); 比较两个内存块前n个字节的大小 一样返回0 (s1>s2)? >0 :<0

char* strerror(int error); 返回error的错误字符串

mode_t umask(mode_t mask)

linux中的 umask 函数主要用于:在创建新文件或目录时 屏蔽掉新文件或目录不应有的访问允许权限。文件的访问允许权限共有9种,分别是:r w x r w x r w x(它们分别代表:用户读 用户写 用户执行 组读 组写 组执行 其它读 其它写 其它执行)。

其实这个函数的作用,就是设置允许当前进程创建文件或者目录最大可操作的权限,比如这里设置为0,它的意思就是0取反再创建文件时权限相与,也就是:(~0) & mode 等于八进制的值0777 & mode了,这样就是给后面的代码调用函数mkdir给出最大的权限,避免了创建目录或文件的权限不确定性。

int getopt(int argc,char * const argv[ ],const char * optstring); https://baike.baidu.com/item/getopt/4705064?fr=aladdin

extern char *optarg;

extern int optind, opterr, optopt;

用作分析命令行参数, argc代表参数个数 argv代表详细参数 optstring代表参数的选项 比如: getopt(argc, argv, "ab:c:de::");

第三参数 单个字符表示一个选项 , 以空格分开每个选项

单个字符后面跟个(冒号:) 表示字符后面必须紧跟一个参数 或者以空格隔开

单个字符后面跟个(双冒号::) 表示可选是否有参数 , 如果有跟参数,该参数必须紧跟选项不能以空格隔开

optarg——指向当前选项参数(如果有)的指针。 optind——再次调用 getopt() 时的下一个 argv 指针的索引。 optopt——最后一个未知选项。

IO函数

FILE* stream != int fd

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

在Linux一切皆文件open可以打开所有文件 , 可以只传前两个参数

flags: O_RDONLY 只读 ,O_WRONLY 只写 , O_RDWR可读写

O_CREAT 不存在则创建 , O_EXCL 判断文件是否存在,存在返回-1 ,一般和O_CREAT一同出现.

O_APPEND 每次读写都会在文件的末尾进行操作

O_NONBLOCK 无论读写对当前文件都是非阻塞的,默认是阻塞的

mode: 0(无权限) 1(执行权限) 2(写权限) 4(读权限) 0671

特殊权限位 文件拥有者权限位 文件拥有者所在组的权限位 其他用户的权限位

一般是0 2+4 = 6可读写 1+2+4 = 7可读可写可执行 1只有执行权限

特殊权限位: 4 临时获得文件拥有者(UID)的权限,使可执行文件在执行阶段具有UID的权限

2 临时获得文件拥有者组(GID)的权限,使可执行文件在执行阶段具有GID的权限

1 使文件只能被该文件的拥有者(UID)或者root用户删除

前提是该文件存在,且只能临时(open和close之间)的获取拥有者权限,不能更改其权限(文件权限除了创建时设置,和 只能被UID和root更改)

具体的mode描述: http://codemouse.online/archives/2020-03-29224238?tdsourcetag=s_pctim_aiomsg

ssize_t (read/write ) (int fd, void *buf, size_t count); 成功 >0 (返回字节数) =0 (可能对端fd关闭) 失败 <0

详细: https://blog.csdn.net/songchuwang1868/article/details/90665865

FILE *fopen(char *filename, char *mode); windows没有fd的概念, 具体: https://blog.csdn.net/fanyun_01/article/details/96971583

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) ptr字符串或数组 , size每个字符的大小 , nmemb写的字符个数 , stream操作的文件

详细: https://blog.csdn.net/fanyun_01/article/details/96971583

size_t fread(const void *ptr, size_t size, size_t nmemb, FILE *stream)ptr存放数据的buff , size每个字符的大小 , nmemb读的字符个数 , stream操作的文件

详细: https://baike.baidu.com/item/fread/10942353?fr=aladdin

int fprintf (FILE* stream, const char*format, [argument]); 将字符串输出到指定文件流里

________________________________________________________________________________

fgetc和getc的区别在于实现方法, fgetc是函数的方式实现的,getc以宏定义的方式实现的 , fputc和putc同理

gets(char* str); 从stdin获取一行(遇到EOF或换行符为一行)字符返回给str,无大小限制

puts(char* str); 把一行字符串str(遇到EOF或换行符为一行)输出到stdout里

fgetc/getc(FILE* stream(比如stdin)); 从指定的文件流中读取一个字符并作为函数返回值(以字符的asiic码值)返回给接收者

fputc/putc(int char ,FILE* stream(stdout)); 把一个字符char(以其asiic的值)输出到指定文件流(stdout)里,再以asiic值代表字符打印

fgets(char* str , int n , FILE* stream) 从文件流中获取一行字符串(遇到换行符,EOF,或达到指定大小n),返回在str , fgets会默认在str结尾加\n

fputs(char* str , FILE* stream) 把一行字符串输出到指定文件流里

_______________________________________如何获得一个文件的大小________________________

int fseek(FILE *stream, long int offset, int whence) 设置指定文件流(stream)里指针的偏移值

whence: 指针偏移的起始位置 SEEK_SET(流的开头) SEEK_CUR(流的当前位置) SEEK_END(流的末尾)

offset: 从whence指定好的偏移位置 开始 ,偏移到指定的偏移量 如,从当前位置偏移 offset = 3 个字符

成功返回0, 失败返回非0值 ,可用perror()打印error值

int ftell(FILE *stream) 返回指定文件流当前的位置距离文件流首位的偏移量

void rewind(FILE *stream) 设置文件流里已经偏移的指针,重新指向文件流的开头

作用 : 获取文件流的实际大小, 可以使用fseek指向末尾,再利用ftell获取开头到末尾的偏移量 , 再用rewind重新指向开头

char *strchr(const char *str, int c) 检索字符串里, 第一次出现指定字符 并返回该位置的指针

socket常用函数

int socket(int af, int type, int protocol)

socket 返回值: 成功 >0 (返回文件描述符的序号) , 失败 -1 ( 创建失败 INVALID_SOCKET也是-1 )

int bind(int sockfd ,const struct sockaddr* my_addr, socklen_t addrlen)

bind 返回值: 成功 0 , 失败 <0 (可用WSAGETLASTERROR 函数取错误码)

int listen( int sockfd, int backlog);

listen 返回值: 成功0 , 失败 <0 (可用WSAGETLASTERROR 函数取错误码)

SOCKET accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

accept 返回值: 成功>0(返回文件描述符的序号一般从3开始) , 失败 <0 (可用WSAGETLASTERROR 函数取错误码)

int connect(SOCKET s, const struct sockaddr * name, int namelen);

connect返回值: 成功 0 , 失败 <0 (可用WSAGETLASTERROR 函数取错误码)

int recv(SOCKET s, charFAR*buf, intlen, intflags);

int recvfrom (int sockfd,void *buf,int buf_len,unsigned int flags,struct sockaddr *from,int *fromlen) flags一般是0

recv/recvfrom():返回值 成功 >0 (返回字符串的len) , =0 (断开连接) ,失败 <0 (可用WSAGETLASTERROR 函数取错误码)

int send( SOCKET s, const char FAR *buf, int len, int flags );

int sendto ( int sockfd , const void * msg, int msg_len, unsigned int flags, const struct sockaddr * to , int tolen ) flags一般是0

send/sendto():返回值 成功 >0 (返回字符串的len) , =0 (断开连接) ,失败 <0 (可用WSAGETLASTERROR 函数取错误码)

recv和recvfrom的区别在于recvfrom可以获取或设置对端的信息(明确发送或接收的目标), send和sendto同理

int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);

sockfd:标识一个套接口的描述字。

level:选项定义的层次;支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6。

optname:需设置的选项。

SO_RCVTIMEO,SO_SNDTIMEO:获得或设置socket发送/接收的timeout。 SO_SNDBUF,SO_RCVBUF:获得或设置socket发送/接收的buffer大小。 SO_BROADCAST:获得或设置socket状况,使之可以广播发送数据报。(只能用于UDP方式)。 SO_REUSEADDR:设置该socket绑定的端口可被重用 防止服务器重启时之前绑定的端口还未释放

optval:指针,指向存放选项待设置的新值的缓冲区。

optlen:optval缓冲区长度。

详细: https://baike.baidu.com/item/setsockopt/10069288?fr=aladdin

________________________________________端口和IP的转换____________________________

htons(uint16_t hostshort): 将主机无符号短整型(uint16_t)转换为网络字节序(大端(0x12 0x34)转小端(0x34 0x12) ),返回转换好的网络字节序

ntohs(uint16_t netshort): 与htons反之, 网络转主机 ,返回转好的主机字节序

htonl(uint32_t hostlong): 将主机无符号长整型(uint32_t)转换为网络字节序(大端(0x12 0x34)转小端(0x34 0x12) ),返回转换好的网络字节序

ntohl(uint32_t netlong): 与htonl反之, 网络转主机 ,返回转好的主机字节序

inet_addr(const char* cp): 将主机点分十进制的ip,转换成无符号长整型(u_long)的二进制的网络字节序

int atoi(const char* nptr): 将字符串转换成整型数 , 返回整型数

int inet_pton(int af, const char *src, void *dst); IP地址 点分十进制 ---> 二进制整数

const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt); 二进制整数 ---> 点分十进制 IP地址

___________________________________设置fd是否阻塞_________________________

int fcntl(int fd, int cmd); cmd: F_GETFL , F_SETFL , F_GETFD , F_SETFDint fcntl(int fd, int cmd, long arg); int fcntl(int fd, int cmd, struct flock *lock);

https://baike.baidu.com/item/fcntl/6860021?fr=aladdin

IO复用

FD_ZERO(fd_set *rfds) 初始化struct fd_set frds(fd集合)

成功 0 ,失败-1

FD_SET(fd,fd_set *rfds) 初始化后把要监听的fd加入到&frds

成功 0 ,失败-1

int select (int maxfd + 1,fd_set *rfds , fd_set *writeset, fd_set *exceptset , const struct timeval * timeout);

监听rfds里fd状态 (读或写或异常状态都会改变),指定时间内 , 以轮循的方式判断每一个fd的状态是否改变, 如果全部无变化select返回0(rfds的里的fd也不做变化) ,如果有N个fd状态改变则保留这些状态改变的fd , 其他无变化的则清除出frds ,并返回留下的数量 (因为select会改变rfds里的fd , 所以每次都要重载rfds)

返回值: 成功>0(返回就绪的fd数量) , =0超时(没有发生读写) ,-1错误

FD_ISSET(fd,fd_set *rfds) 判断指定fd是否存在frds里,如果存在说明fd状态发生改变,则可以处理当前指定的fd , 不存在说明没有变化,不做处理

存在返回>0 , 不存在返回0 , 失败 -1

小知识:在比较多fd的情况下可以把fd都加入到一个fd数组里 , 以循环(for)的方式吧数组内的fd加入到rfds里,避免一行行的FD_SET(fd)

_____________________________________epoll_______________________________

int epoll_create(int size); 设置监听容器的最大数量

struct epoll_event

{

uint32_t events; /* 事件的类型 */

epoll_data_t data; /* 事件的信息 */

} __attribute__ ((__packed__));

typedef union epoll_data

{

void *ptr;

int fd;

uint32_t u32;

uint64_t u64;

} epoll_data_t;

struct epoll_event ep_ev

ep_ev.events = EPOLLIN

ep_ev.data = fd 或者 FILE*的指针

epoll_event

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 把要监听的fd及其类型 加入到epoll_create()返回的容器里

op: EPOLL_CTL_ADD:注册新的fd到epfd中; EPOLL_CTL_MOD:修改已经注册的fd的监听事件; EPOLL_CTL_DEL:从epfd中删除一个fd;

event.events: EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭); EPOLLOUT:表示对应的文件描述符可以写; EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来); EPOLLERR:表示对应的文件描述符发生错误; EPOLLHUP:表示对应的文件描述符被挂断; EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的。 EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout); 在timeout的时间里进行对 epfd容器 的监听 events存放wait监听到的已经触发的就绪事件 maxevents是events存放的最大数量 后续就可以对events里的就绪事件进行判断并处理了

Linux信号

int kill(pid_t pid,int sig); 成功返回0,失败返回-1 给指定进程pid发送指定信号signo

int raise(int sig); 成功返回0,失败返回-1 给当前进程发送指定信号

sig_t signal(int signum,sig_t handler); 给指定信号绑定指定执行函数 , sigkill , sigstop不能被更改

unsigned int alarm(unsigned int seconds); 指定一个时间,程序的执行时间到达指定时间后给当前进程发送 SIGALRM , alarm会覆盖前一个alarm

程序开始执行时有一个jiffies会从0累加时间直到程序结束, 而alarm的指定时间是从jiffies的0开始计算的,

useconds_t ualarm(useconds_t usecs , useconds_t interval); 程序在触发第一个 SIGALRM 后每间隔一个时间段(interval)执行一次 SIGALRM

int setitimer(int which, const struct itimerval new_value, struct itimerval *old_value) ,创建比alarm()更精确的定时器 old_value旧的定时器时间可以NULL

int getitimer(int which, struct itimerval *curr_value); ,curr_value 设置指定开始时间和间隔时间

which: ITIMER_REAL 以系统真实的时间来计算,它送出SIGALRM信号 ,三种定时器一般使用第一个就可以了

ITIMER_VIRTUAL 以该进程在用户态下花费的时间来计算,它送出SIGVTALRM信号

ITIMER_PROF 以该进程在用户态下和内核态下所费的时间来计算,它送出SIGPROF信号

____________________比signal更多功能局限也比signal大,可以对执行函数进行传参_____

int sigaction(int signum , const struct sigaction *act , struct sigaction *oldact); 绑定信号函数,和设置信号屏蔽集, 也可以保存信号之前的执行函数方便恢复

struct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void); }

小知识: void sigaction的第三参数可以作为 sa_sigaction的第三参数的传参,比如一个指针

int sigqueue(pid_t pid, int sig, const union sigval val); 给指定pid发送指定sig并且可以带数据sigval 的发送信号

typedef union sigval { 可以存放4字节的数据可以跨进程 但指针只能在同一进程内传递跨进程则无效

int sival_int;

void *sival_ptr;

}sigval_t;

sigqueue发送的数据可以通过信号函数的 sigaction_t* info.si_int 或 info->si_value.sival_int 获取

_________________对信号屏蔽集(sa_mask)的操作函数_______________

int sigemptyset(sigset_t*set); 清空信号集合 成功返回0 失败则返回-1int sigfillset(sigset_t* set); 填满信号集合(把所有信号都放进去) 成功返回0 失败则返回-1int sigaddset(sigset_t* set, int signum); 往set集合里追加signum 成功返回0 失败则返回-1int sigdelset(sigset_t* set, int signum); 把signum从set中删除 成功返回0 失败则返回-1int sigismember(const sigset_t *set, int signum); 测试set里是否有signum 有则返回1 无则返回0 失败则返回-1

int sigprocmask(int how, const sigset_t newset, sigset_t *oldset); 第三个参数作备份用的 how = SIG_BLOCK 将设置好的信号屏蔽集newset加入到当前进程的屏蔽集里 SIG_UNBLOCK 将设置好的信号屏蔽集newset从当前进程的屏蔽集里删除 SIG_SETMASK 将设置好的信号屏蔽集newset设置为当前进程的屏蔽集

int sigsuspend(const sigset_t *mask); 信号阻塞, 在收到指定信号集里的信号前 程序会阻塞,直到收到信号为止

int sigpending(sigset_t *set); 查询有多少未决信号

aio编程

struct aiocb {

int aio_fildes; /* 文件描述符 */ off_t aio_offset; /* 文件偏移 */ volatile void *aio_buf; /* 缓冲区地址 */ size_t aio_nbytes; /* 传输的数据长度 */ int aio_reqprio; /* 请求优先级 */ struct sigevent aio_sigevent; /* 通知方法 */ int aio_lio_opcode; /* 仅被 lio_listio() 函数使用 AIO_READ , AIO_WRITE*/

};

int aio_read(struct aiocb *aiocbp) 对aiocb对象发起读操作 , 不会阻塞进程,内核会继续完成操作

int aio_write(struct aiocb *aiocbp) 对aiocb对象发起写操作, 不会阻塞进程,内核会继续完成操作

int aio_error( struct aiocb *aiocbp ); 主动发起询问aio_read()和aio_write()的返回状态的请求

返回值 EINPROGRESS,说明请求尚未完成

ECANCELLED,说明请求被应用程序取消了

-1 返回错误:errno , 0 说明完成当前请求

ssize_t aio_return( struct aiocb *aiocbp ); 接收读写操作的返回状态, 要留足够时间等待操作的完成才接收,不然会因为异步(操作未完成)而接收到完成前返回状态

int aio_suspend(const struct aiocb *const cblist[], int n, const struct timespec *timeout); 阻塞函数,直到指定的aiocb异步操作完成或超时才会返回

struct timespec { timeout给NULL就可以了

time_t tv_sec; // seconds long tv_nsec; // and nanoseconds };

第一个参数是 把要操作的 aiocb对象加入到 cblist[] 这个集合里

第二个参数为要操作的 aiocb 对象的个数, 第三个参数为等待阻塞的超时时间,NULL为无限等待

有三种返回值 1.超时了 , 2.出错了error , 3.aio全部都返回了

int lio_listio(int mode,struct aiocb *list[],int nent,struct sigevent *sig); 处理批量的 aiocb 对象

mode: LIO_WAIT 阻塞发起 阻塞等到所有发起的AIO全部完成后,才会返回

LIO_NOWAIT 非阻塞发起 发起后立即返回,通过绑定的信号来通知

struct sigevent {

int sigev_notify; //应用线程的通知类型 一般使用默认选项 SIGEV_THREAD 创建一个线程

int sigev_signo; //应用线程的信号

union sigval sigev_value; //应用线程的值

void (*sigev_notify_function)(union sigval); //应用线程的回调函数

pthread_attr_t *sigev_notify_attributes; //应用线程的属性 一般使用默认的 NULL };

lio_listio() LIO_WAIT 阻塞模式 , 给aiocb->aio_opcode指定类型(AIO_READ或AIO_WRITE), lio_listio()会根据指定类型做出对 aiocb->aio_filedes 指向的文件句柄的相应操作(不用手动的aio_read了只管aio_return()后打印数据了), 且完成操作函数才会返回,否则函数会一直阻塞

lio_listio() LIO_NOWAIT 非阻塞模式 , 无需给aiocb指定类型 , 但是需要用到 sigevent ,给aiocb绑定回调函数, 当主进程发起aio_read或者aio_write时,会触发回调函数, 主进程不阻塞, 但因为是异步操作 所以 主进程不能比AIO线程更快的的结束 否则得到的数据不匹配

POSIX文件操作和目录管理

POSIX是可移植操作系统接口(Portable Operating System Interface for UNIX)的缩写

POSIX文件操作:

在移动开发中,难免要做一些跨平台的事情。iOS和Android各执一套,平台很难跨。但其底层Linux和Unix则早早做了一套不错的兼容标准。这套标准就是POSIX , 其实就是 open 设置文件权限 read write lseek(文件数据的偏移值) 等这些可以对所有文件都可以操作的函数

详细: https://www.jianshu.com/p/b86298645e08

目录管理:___________________________________________________________________

int stat(const char *file_name, struct stat *buf); 通过文件名filename获取文件信息,并保存在buf所指的结构体stat中

int lstat(const char *path, struct stat *buf); 当文件是一个符号链接时,lstat返回的是该符号链接本身的信息;而stat返回的是该链接指向的文件的信息

int fstat(int filedes, struct stat *buf); 和上两个相比 只能打开文件句柄, 不能打开带路径的文件

struct stat

{

dev_t st_dev; /* 设备号码 特殊的文件*/

ino_t st_ino; /* inode节点号 常规的文件*/

mode_t st_mode; /* 文件对应的模式 , 文件 , 目录等 */

nlink_t st_nlink; /* 文件的连接数*/

uid_t st_uid; /* 文件的所有者 */

gid_t st_gid; /* 文件所有者对应的组 */

dev_t st_rdev; /* 特殊设备号码 */

off_t st_size; /* 普通文件 , 对应文件字节数 */

blksize_t st_blksize; /* 文件内容对应的块大小 , 比如磁盘的读写单位都是按块计算的*/

blkcnt_t st_blocks; /* 文件内容对应块的数量 */

time_t st_atime; /* 文件最后被访问的时间 */

time_t st_mtime; /* 文件最后被修改的时间 */

time_t st_ctime; /* 文件状态改变的时间 */

};

01.S_IFMT 0170000 文件类型的位遮罩

02.S_IFSOCK 0140000 socket

03.S_IFLNK 0120000 符号链接(symbolic link)

04.S_IFREG 0100000 一般文件

05.S_IFBLK 0060000 区块装置(block device)

06.S_IFDIR 0040000 目录

07.S_IFCHR 0020000 字符装置(character device)

08.S_IFIFO 0010000 先进先出(fifo)

09.S_ISUID 0004000 文件的(set user-id on execution)位

10.S_ISGID 0002000 文件的(set group-id on execution)位

11.S_ISVTX 0001000 文件的sticky位

12.S_IRWXU 00700 文件所有者的遮罩值(即所有权限值)

13.S_IRUSR 00400 文件所有者具可读取权限

14.S_IWUSR 00200 文件所有者具可写入权限

15.S_IXUSR 00100 文件所有者具可执行权限

16.S_IRWXG 00070 用户组的遮罩值(即所有权限值)

17.S_IRGRP 00040 用户组具可读取权限

18.S_IWGRP 00020 用户组具可写入权限

19.S_IXGRP 00010 用户组具可执行权限

20.S_IRWXO 00007 其他用户的遮罩值(即所有权限值)

21.S_IROTH 00004 其他用户具可读取权限

22.S_IWOTH 00002 其他用户具可写入权限

23.S_IXOTH 00001 其他用户具可执行权限

mode_t st_mode

在Linux一切皆文件, 而区别这些文件的类型则保存在 mode_t ,目前,st_mode使用了其低19bit. 0170000 => 1+ 3*5 = 16.其中,最低的9位(0-8)是权限,9-11是id,12-15是类型。

S_ISLNK(st_mode) :是否是一个连接. 是为1 否为0 S_ISREG(st_mode) :是否是一个常规文件. S_ISDIR(st_mode) :是否是一个目录 S_ISCHR(st_mode) :是否是一个字符设备. S_ISBLK(st_mode) :是否是一个块设备 S_ISFIFO(st_mode) :是否 是一个FIFO文件. S_ISSOCK(st_mode) :是否是一个SOCKET文件

DIR * opendir(const char * name); 打开目录

struct dirent * readdir(DIR * dir); 读取目录信息

struct dirent

{

ino_t d_ino;

ff_t d_off;

signed short int d_reclen;

unsigned char d_type;

har d_name[256];

};

struct dirent

int closedir(DIR * dir); 关闭目录

详细 : https://www.iteye.com/blog/lobert-1706165

int mkdir(const char *pathname, mode_t mode); 以mode方式创建一个以参数pathname命名的目录,mode定义新创建目录的权限。和open的mode一样

原始套接字

什么是原始套接字: https://www.linuxidc.com/Linux/2015-04/115683.htm

int socket(AF_INET, SOCK_RAW, int protocol); SOCK_RAW原始套接字要在root下运行, 但可以临时提升到文件拥有者权限 , 比如ping

原始套接字最重要的在于对 协议的了解 并基于这些协议自行构建数据包, 比如 ICMP协议

进程通信

僵死进程: 一个进程使用fork创建父子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。(子进程退出后子进程的资源得不到释放,这些称为僵死进程,由父进程用wait确认子进程状态,并释放资源)

孤儿进程: 一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。(init进程: https://baike.baidu.com/item/init%E8%BF%9B%E7%A8%8B/3042859?fr=aladdin)

守护进程: fork()一个进程 父子进程有一个关联那就是在同一个会话组里, 而结束父进程后,子进程将setsid()一个新的会话组并作为该组的组长, 子进程将摆脱了和父进程的关联, 并在后台运行 这样的进程称为守护进程

进程状态: https://www.cnblogs.com/diegodu/p/9167671.html

_______________________________________________

pid_t fork(void); 创建父进程和子进程 , 父进程保存子进程的pid 子进程0 ,父子进程会复制主进程资源两份,各自一份,互不影响

int wait(int* statloc); 阻塞父进程直到其所有子进程全部退出 ,由父进程确认子进程状态,如果是退出状态则释放子进程的资源,并将状态返回给*statlocint waitpid(pid_t pid, int* statloc, int options); 阻塞当前进程并等待目标pid结束进程, 可以由 options决定是否阻塞

wait和waitpid的区别: https://blog.csdn.net/csdn_kou/article/details/81091191

pid_t getpid(void); 获得当前进程的 idpid_t getppid(void); 获得父进程的 id

pid_t getsid(pid_t pid) 获取当前会话的ID

setsid() 将调用进程设置成新的会话组组长, 直接调用不需要返回值和参数

setuid() 将当前进程设置为文件拥有者权限

int socketpair(int domain, int type, int protocol, int sv[2]); 建立一对匿名的已经连接的套接字,可通过fd句柄进行亲缘关系进程间的通信,是双向的

pair: https://blog.csdn.net/wufuhuai/article/details/79747912

int pipe(int *pipe_fd); 创建无名管道文件, 通过fd的两端进行传输数据 单向且只能是亲缘关系进程, 主进程关闭也会消失, 简单,传输快

int mkfifo(const char* pathname, mode_t mode); 创建有名管道文件 , 可以进行非亲缘关系的进程通信, 文件不会随着进程的消失而消失,会存在于文件系统中, 可多次使用 ,仅限于两个进程一对一, 和普通文件一样的用法,open后往文件fd read/write数据(文件不具备存储性,只有流通性), 传输慢,量小 ,open fifo文件的时候进程会阻塞,直到有另外一个进程同时open同一个fifo文件 ,在共享文件夹里无法创建fifo

exec函数族: https://blog.csdn.net/zhengqijun_/article/details/52852074

execl(const char *path, const char *arg, ...) 传的参是文件路径 第二个开始的参数数量是可变的,arg0 ,arg1 ... 一般都使用execl

execv(const char *path, char * const argv[]);传的参是文件路径 ,第二个参数是一个指针数组不用同上一样一个个的手动传参

execlp(const char *file, const char *arg, ...)传的参是文件名(系统会从$PATH里找到目录) l使用路径 p使用文件名根据环境变量PATH

execve(const char *path, char * const argv[], char* const envp[](指定环境变量))是真正意义上的系统调用,其他的都是在此基础上封装的库函数

https://blog.csdn.net/David_361/article/details/86057551

int access(const char *filenpath, int mode); 或者int _access( const char *path, int mode ); 判断文件的权限, 存在指定的权限返回0, 不存在 -1

R_OK 只判断是否有读权限

W_OK 只判断是否有写权限

X_OK 判断是否有执行权限

F_OK 只判断是否存在

______________________XSI消息队列:_______________________________

和管道的区别, msgsnd的data会一直存在于队列里, 每次打开消息队列都可以 msgrcv 消息队列里的data , 且消息队列创建后会一直存在

struct msqid_ds { 在内核中每个消息队列都是以msqid_ds这样的结构体进行管理的, 可通过msgrcv等这样的操作函数对当前结构体进行读取或写入数据,和获取当前消息的队列的一些信息属性

struct ipc_perm msg_perm; /* Ownership and permissions */ messagequeue的权限

三个time主要用来监控消息队列什么时候有数据什么时候发送数据什么时候接收数据

time_t msg_stime; /* Time of last msgsnd(2) */ 最后一次的发送msg时间

time_t msg_rtime; /* Time of last msgrcv(2) */ 最后一次的接收msg时间

time_t msg_ctime; /* Time of last change */ 最后一次的msg改变的时间

unsigned long __msg_cbytes; /* Current number of bytes in queue的数目 具体有多少个msqid_ds结构体

queue (nonstandard) */

msgqnum_t msg_qnum; /* Current number of messages 消息的数目 代表msqid_ds里有多少个msg

in queue */

msglen_t msg_qbytes; /* Maximum number of bytes 队列中允许的最大字节数

allowed in queue */

pid_t msg_lspid; /* PID of last msgsnd(2) */ 最后一个发送进程

pid_t msg_lrpid; /* PID of last msgrcv(2) */ 最后一个接收进程

struct msg * msq_first ; 消息队列里指向第一个消息

struct msg * msq_last ; 指向消息队列里最后一个消息, 可通过msgctl对消息队列进行增删改查

};

struct ipc_perm //拥有者及权限的对象

{

__kernel_key_t key;

__kernel_uid_t uid; //用户\创建者

__kernel_gid_t gid; //用户组

__kernel_uid_t cuid; //当前有效ID

__kernel_gid_t cgid; //当前有效组ID

__kernel_mode_t mode;

nsigned short seq;

};

View Code

key_t ftok(const char *pathname, int proj_id); 根据目录返回一个key值

int msgget(key_t key ,int megflag) 基于key值创建一个消息队列并返回一个int值作为句柄

flag : #define IPC_CREAT 00001000 /* 创建 */

#define IPC_EXCL 00002000 /* 如果key存在则失败 */

#define IPC_NOWAIT 00004000 /* return error on wait */

msgget详细: https://baike.baidu.com/item/msgget/322046?fr=aladdin

int msgctl(int msgqid, int cmd, struct msqid_ds *buf); 消息队列控制函数

cmd: IPC_STAT :读取消息队列的 msqid_ds 属性,保存在msqid_ds *buf里

IPC_SET : 设置消息队列属性 ,给msgqid指定 msgqidmsqid_ds *buf信息

IPC_RMID :删除消息队列

IPC_INFO :Linux特有命令,获取并保存 msginfo结构体里的全部信息,保存在buf中,

IPC_INFO (Linux-specific)

Returns information about system-wide message queue limits and parameters in the structure pointed to by buf. This structure is of type msginfo (thus, a cast is required), defined in if the _GNU_SOURCE feature test macro is defined:

msgctl详细: https://baike.baidu.com/item/msgctl/715680?fr=aladdin

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

msgp:用户定义的缓冲区(可以自定义一个struct{type,data};) , msgsz:消息内容长度 ,

msgflg: , 0:消息队列满时msgsnd阻塞 , IPC_NOWAIT:队列满时msgsnd不阻塞立刻返回

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

msgtyp: 0 接收第一条消息 , >0接收类型等于msgtyp的第一个消息 , <0接收类型等于或者小于msgtyp绝对值的第一个消息

msgsnd/msgrcv详细: https://baike.baidu.com/item/msgsnd%2Fmsgrcv/934004?fr=aladdin

消息队列多次调试时要确保消息队列是否存在,如果不存在再创建如果存在则继续使用已经存在的消息队列 , 因为消息队列一经创建是会一直存在下去的, 且msgsnd的消息也会一直存在于消息队列里 , 就算关闭发送端,也是可以多次msgrcv的

有些判断是需要根据返回值健全的处理方式,而不是置之不理,

___________________________XSI信号量_______________________________________

信号量的概念: https://blog.csdn.net/Code_ZX/article/details/85008036

信号量是解决进程间的同步于互斥的问题 , 信号量代表了进程可消耗的资源个数,比如信号量的初始值是0, semop+1 后其他进程可以根据值进行处理(同步这也是生产者和消费者的概念, 先有可消耗资源,再消耗资源,至于要消耗多少具体就看有多少可消耗的)

struct semid_ds { 在内核中每个信号量都是这样的结构体进行管理,

可通过semop等这些操作函数,对当前结构体的数据进行更改或获取,

具体可以在gdb的时候 p (struct semid_ds) sem_id 进行查看

struct ipc_perm sem_perm; /* Ownership and permissions */

time_t sem_otime; /* Last semop time */

time_t sem_ctime; /* Last change time */

unsigned long sem_nsems; /* No. of semaphores in set */ //信号量的个数

struct sem * sem_base //信号量链表的头指针

};

struct sem{

int semval 信号量的值 ,代表当前信号可消耗资源的个数 ,如果是1那么当前信号可以用1次 ,如果为0当前信号不能进行消耗操作

int sempid 最近一个操作的进程号

}

semid_ds

union semun { //在 Linux内核2.6 该共用体已被注释需要手动定义共用体

int val; /* Value for SETVAL */

struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */

unsigned short* array; /* Array for GETALL, SETALL */

struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */

};

int semget(key_t key,int n,int semflag); 创建一个sem

nsems:创建的信号量集中的信号量的个数,该参数只在创建信号量集时有效, 其他和msgget()一样

semget详细: https://baike.baidu.com/item/semget/7009668?fr=aladdin

int semctl(int semid,int semnum,int cmd, /*union semun arg*/); //可以对 (struct semid_ds)semid这个对象进行操作

semnum = 0 代表第一个信号量,以此类推, arg是获取或设置信号量的值 , 可以不传参

semctl详细: https://baike.baidu.com/item/semctl/4840875?fr=aladdin

int semop(int semid,struct sembuf *sops,size_t nsops) //修改信号量的值

sops: 设置从初始值开始要加或者减的值 , nsops: 信号操作结构的数量,恒大于或等于1。

semop详细: https://baike.baidu.com/item/semop/8901332?fr=aladdin

PV原子操作(不可中断或一系列的操作): P:有可用资源 V:生产资源

_________________________________________XSI共享内存_________________________________

struct shmid_ds { 共享内存在内核的对象

struct ipc_perm shm_perm; /* operation perms */

int shm_segsz; /* size of segment (bytes) */

__kernel_time_t shm_atime; /* last attach time */

__kernel_time_t shm_dtime; /* last detach time */

__kernel_time_t shm_ctime; /* last change time */

__kernel_ipc_pid_t shm_cpid; /* pid of creator 创建者*/

__kernel_ipc_pid_t shm_lpid; /* pid of last operator 最后一次操作的pid*/

unsigned short shm_nattch; /* no. of current attaches */

};

shmid_ds

不属于任何一个进程,是内核创建的一块共享的内存 ,生命周期和进程相同, 共享内存是内核的资源会以映射的方式让进程访问, 不会使用到进程的资源(要做好互斥), 访问操作的内存是在内核里,而非进程的

int shmget(key_t key, size_t size, int shmflg); 基于key创建共享内存

size一般就几K共享内存不会太大 shmflg: IPC_CREAT | IPC_EXCL | 0666

shmget详细: https://baike.baidu.com/item/shmget/6875075?fr=aladdin

int shmctl(int shmid, int cmd, struct shmid_ds *buf); 操作(设置,获取,删除)共享内存

cmd: IPC_STAT 获取 shmid 的 shmid_ds ,保存在buf里

IPC_SET 把buf 设置成shmid 的 shmid_ds

IPC_RMID 删除shmid和他的shmid_ds

SHM_LOCK 给共享内存上锁 这并不代表一个进程锁定住共享内存以后其它进知程禁止访问。锁定后只是不会交换到文件系统里(虚拟内存,swap分区)道,而不是指它的访问性, 如果要限制访问性,可以使用信号量

SHM_UNLOCK 解锁

shmctl详细: https://baike.baidu.com/item/shmctl/5391631?fr=aladdin

void *shmat(int shmid, const void *shmaddr, int shmflg); 开始访问共享内存

shmaddr: 指定共享内存映射到进程虚拟内存的首地址, 通常为NULL 让内核分配地址

shmflg: SHM_RDONLY 以只读的方式其他为读写方式 一般是 0

SHM_REMAP 以重映射的方式

SHM_EXEC 以可执行方式

shmat详细: https://baike.baidu.com/item/shmat/6877340?fr=aladdin

int shmdt(const void *shmaddr); 结束使用共享内存detach

shmdt详细: https://baike.baidu.com/item/shmdt/7255690?fr=aladdin

_________________________域套接字和sendmsg_______________________________________________

如何在进程间传递一个文件呢, 使用本地sicket和sendmsg进行通信传输

把struct msghdr 对象填满, 再使用send/recvmsg() 进行传输任何东西包括文件句柄

ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); 什么都可以传递(文件句柄,指针,字符串等)

ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

//可以传输有效数据也可以传递控制数据

struct msghdr {

void *msg_name; /* optional address 网络套接字时指向目标 struct sockaddr_in 本地套接字是可以0/

socklen_t msg_namelen; /* size of address struct sockaddr_in的长度*/

struct iovec *msg_iov; /* scatter/gather array 指向有效数据iov的首地址*/

size_t msg_iovlen; /* # elements in msg_iov iov的个数*/

void *msg_control; /* ancillary data, see below 指定控制数据(一般进程间发送文件句柄时使用)*/

size_t msg_controllen; /* ancillary data buffer len 控制数据的大小 == cmsg_len */

int msg_flags; /* flags on received message 可自定义一个类型来区别消息类型,可选择是否接收,一般可以0*/

};

struct iovec { /* Scatter/gather array items 可以是指针, 字符串*/

void *iov_base; /* Starting address 本地socket 发送无限制 , 网络socket 内存地址是无效的*/

size_t iov_len; /* Number of bytes to transfer base的sizeof()*/

};

struct cmsghdr { /*控制数据的载体*/

socklen_t cmsg_len; /* data byte count, including hdr 控制数据的大小,包含头(sizeof(struct cmsghdr)+/*sizeof(cmsg_data)*/)*/

int cmsg_level; /* originating protocol 控制信息的级别(如: SOL_SOCKET)*/

int cmsg_type; /* protocol-specific type 控制消息的类型(如: SCM_ RIGHTS)*/

/* followed by unsigned char cmsg_data[]; */ 这个成员并不实际存在。他用来指明实际的额外附属数据所在的位置 可以使用CMSG_DATA(cmsg)获得cmsg_data的首地址, 可以使用memcpy()进行赋值没有类型限制

};socket(AF_LOCAL,SOCK_STREAM,0);struct sockaddr_un local;int unlink(const char *pathname); 删除指定文件, Linux一切皆文件 unlink : https://blog.csdn.net/qq_35733751/article/details/80889289

多线程编程

pthread_t pthrad_self(void); 获得线程tid(由线程库维护的 ,其ID空间的各个进程独立的, ,因此每个进程的线程可能会相同)

syscall(SYS_gettid); 获取内核中的线程ID(内核中没有线程的概念只有轻量级进程 ,SYS_gettid则是获取内核中这个轻量级进程的ID 是唯一的)

int syscall(int number , ...) 或者 pid_t gettid(void); 上面的系统调用作用一样

线程的操作函数:

int pthread_create(pthread_t* tidp , const pthread_attr_t* attr , void* (start_routine)(void*) , void* arg); 创建子线程

参数1:指向线程的标识符 2:设置线程的属性 3:线程的执行函数的首地址 4:执行函数的参数

void pthread_exit(void* retval); 退出当前线程并返回*retval

int pthread_join(pthread_t thread, void **retval); 阻塞等待指定线程退出,并接收子线程pthread_exit()返回的指针

int pthread_cancel(pthread_t thread); 对指定线程发出cancel信号

int pthread_setcancelstate(int state, int *oldstate); 设置当前线程的取消状态(是否可取消)

state : PTHREAD_CANCEL_ENABLE(缺省) 设置成可取消状态, 收到cancel信号后线程可被取消

PTHREAD_CANCEL_DISABLE 设置成不可取消状态, 收到cancel信号后会忽略信号继续运行,直到线程恢复可取消状态

也可通过这函数获得线程状态 利用oldstate返回的状态, state给NULL

int pthread_setcanceltype(int type, int *oldtype); 设置当前线程的 "取消" 类型

type : PTHREAD_CANCEL_DEFERRED 当前线程收到取消信号后先放入队列中, 在线程的 可取消状态下 线程才会取消

PTHREAD_CANCEL_ASYNCHRONOUS 线程可在任何时间和状态, 立刻取消当前线程

void pthread_testcancel(void) 检测当前线程是否为可取消状态, 是则进行取消动作 , 否则直接返回 (我也不晓得有什么用,不是很重要的函数)

线程分离:不需要pthread_join()来释放线程结束后的资源, 设置线程分离 线程结束后会自动释放资源:

https://www.cnblogs.com/x_wukong/p/7976521.html

int pthread_attr_init(pthread_attr_t*attr);

int pthread_attr_setdetachstate(pthread_attr_t *attr , int detachstate);

int pthread_attr_getdetachstate(pthread_attr_t *attr , int* detachstate);

int pthread_detach(pthread_t th);

设置线程为分离状态(detach)有两种方式一种

1.pthread_detach(pthread_self())在线程函数里执行把当前线程设置为分离状态,当前线程结束后会自动释放资源

2.在线程创建时设置线程属性为detach状态

pthread_attr_t attr;

pthread_attr_init(&attr);

pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); PTHREAD_CREATE_JOINABLE

pthread_create(&tid, &attr, THREAD_FUNCTION, arg);

_________________________________________________________________________

线程的私有数据TSD: 该数据仅限线程所有的全局变量

pthread_key_t key 私有数据TSD 对象

int pthread_key_create(pthread_key_t key, void (destructor)(void*)); 创建TSD(Thread-specific Data)

int pthread_setspecific(pthread_key_t key, const void *value); 设置TSD

void *pthread_getspecific(pthread_key_t key); 获取TSD, 返回一个void*类型的指针(指向一个地址) , 要取值的话加个*( (int*)pthread_getspecific() )或者int * temp = (int*)pthread_getspecific()

int pthread_key_delete(pthread_key_t key); 删除TSD

____________________________________________________________________

线程的互斥: 细分为: 互斥锁 条件变量 , 读写锁

互斥锁:

pthread_mutex_t my_mutex 互斥锁对象

有两种方法创建互斥锁,静态方式和动态方式。POSIX定义了一个宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥锁,方法如下:

pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;

在LinuxThreads实现中,pthread_mutex_t是一个结构,而PTHREAD_MUTEX_INITIALIZER则是一个结构常量。

动态方式是采用pthread_mutex_init()函数来初始化互斥锁

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_trylock(pthread_mutex_t *mutex); 非阻塞上锁 , 互斥锁没上锁则上锁并返回, 已上锁也立刻返回

int pthread_mutex_unlock(pthread_mutex_t *mutex); 解锁

互斥锁的在进程里的作用域等同于(全局变量和局部变量)的作用域的区别,

struct node

{

pthread_mutex_t m_mutex;

char data[10];

};

struct node buffer;

int t_data = 0;

node里的锁不会影响到 t_data

该属性根据需求来设置,非必要

pthread_mutexattr_t mattr 互斥锁对象的属性

pthread_mutexattr_init(&mattr); 初始化互斥锁属性

int pthread_mutexattr_setpshared(pthread_mutexattr_t *mattr, int pshared); 设置互斥锁的属性

pshared: PTHREAD_PROCESS_SHARED 进程间互斥锁,多个进程共享(仅限亲缘关系的进程)

PTHREAD_PROCESS_PRIVATE 进程内互斥锁,仅当前进程共享

int pthread_mutexattr_destroy(pthread_mutexattr_t *mattr) 销毁对象

条件变量: 条件变量必须配合互斥锁使用

pthread_cond_t condtion 条件变量对象

int pthread_cond_init(pthread_cond_t * cond,const pthread_condattr_t * attr); 初始化对象

int pthread_cond_destroy(pthread_cond_t *cond); 销毁对象

int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex); 条件不满足时阻塞线程, 等待cond_signal唤醒(condtion), 并且会暂时的自动解锁互斥锁,让其他线程访问共享区域

int pthread_cond_timedwait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex,const struct timespec *restrict abstime); ,加上了时间的限制

int pthread_cond_signal(pthread_cond_t *cond); 条件满足时可以发送信号唤醒 单个pthread_cond_wait()

int pthread_cond_broadcast(pthread_cond_t *cond); 同时唤醒多个线程中的pthread_cond_wait()

读写锁: 对于读数据比修改数据频繁的应用,用读写锁代替互斥锁可以提高效率。

pthread_rwlock_t rwlock 读写锁对象

int pthread_rwlock_init(pthread_rwlock_t *restrict 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_tryrdlock(pthread_rwlock_t *rwlock); 非阻塞的开启读锁

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); 阻塞的开启写锁

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); 非阻塞的开启写锁

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock); 解锁

_______________________________________________________________________

线程的同步: 进程信号量是有名信号量 进行通信 , 而线程的信号量是无名信号量 进行通信

互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源

#include :https://baike.baidu.com/item/sem_t/6876569?fr=aladdin

int sem_init (sem_t *sem, int pshared, unsigned int value);

信号量的类型pshared: 为0时仅当前进程的线程间共享 , 非0时则进程间共享 value: 为信号量的初始值

int sem_wait(sem_t * sem);

如果信号量非0值时,对信号量进行-1操作 , 为0值时阻塞直到信号量非0值

int sem_post(sem_t * sem);

对信号量进行+1操作

_________________________________________________________________________

多线程信号:

同一个进程下的所有线程共享 信号的处理方式,所以其中一个线程改变某个信号处理都会影响到同一进程下的所有线程的处理方式

https://blog.csdn.net/rikeyone/article/details/89203836

int pthread_kill(pthread_t thread, int signum); 对指定线程发出信号

int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset); 设置屏蔽信号集

how: SIG_BLOCK 将设置好的信号屏蔽集set加入到当前进程的屏蔽集里 SIG_UNBLOCK 将设置好的信号屏蔽集set从当前进程的屏蔽集里删除 SIG_SETMASK 将设置好的信号屏蔽集set设置为当前进程的屏蔽集

在多线程的程序里,希望只在主线程中处理信号,可以使用pthread_sigmask()

sigprocmask 只能用于单进程单线程; fork的子进程拥有一份屏蔽信号拷贝;

pthread_sigmask 用于多线程 ; 每个线程都有自己的mask, 但是信号的处理;是所有线程共享的, 子线程的mask从主线程继承而来

int sigwait(const sigset_t *set , int * sig); 信号同步处理

由于每个线程都可能接收到信号,当多线程收到信号时,我们是无法确定是哪一个线程处理这个信号,因此多线程的异步信号处理就显得很复杂,而sigwait可以实现信号的同步处理。

set是sigwait所等待的信号集(并非屏蔽的信号集), sig是sigwait返回收到的signum 比如是被SIGUSR1唤醒的,那么sig就会返回SIGUSR1代表的10