目錄: 1.普通的IO函數
_________________________________________其他函數___________________________
宏(包括函數式宏) 沒有返回值的概念,因為它只是在源代碼文件(級別)上的文本替換(即修改些源代碼),沒有一個切換並保護現場的過程,所以不需要也不存在一個通過返回值將其的結果傳送回來的過程。還有,宏所代表的表達式的計算結果是叫一個結果值,不叫(函數)返回值。例如, #define A(a,b,c) ({a=1;b+=1;c=3;a+b+c;}) #include <stdio.h> 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; }
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給出最大的權限,避免了創建目錄或文件的權限不確定性。
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) 檢索字符串里, 第一次出現指定字符 並返回該位置的指針
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 函數取錯誤碼)
SO_SNDBUF,SO_RCVBUF:獲得或設置socket發送/接收的buffer大小。
SO_BROADCAST:獲得或設置socket狀況,使之可以廣播發送數據報。(只能用於UDP方式)。
SO_REUSEADDR:設置該socket綁定的端口可被重用 防止服務器重啟時之前綁定的端口還未釋放
詳細: https://baike.baidu.com/item/setsockopt/10069288?fr=aladdin
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_SETFD
int 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
FD_ZERO(fd_set *rfds) 初始化struct fd_set frds(fd集合)
成功 0 ,失敗-1
FD_SET(fd,fd_set *rfds) 初始化后把要監聽的fd加入到&frds
成功 0 ,失敗-1
監聽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*的指針
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里的就緒事件進行判斷並處理了
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 失敗則返回-1
int sigfillset(sigset_t* set); 填滿信號集合(把所有信號都放進去) 成功返回0 失敗則返回-1
int sigaddset(sigset_t* set, int signum); 往set集合里追加signum 成功返回0 失敗則返回-1
int sigdelset(sigset_t* set, int signum); 把signum從set中刪除 成功返回0 失敗則返回-1
int 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); 查詢有多少未決信號
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是可移植操作系統接口(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 其他用戶具可執行權限
在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]; };
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); 阻塞父進程直到其所有子進程全部退出 ,由父進程確認子進程狀態,如果是退出狀態則釋放子進程的資源,並將狀態返回給*statloc
int 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); 獲得當前進程的 id
pid_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
______________________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; };
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 <sys/msg.h> 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 最近一個操作的進程號 }
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 */ };
不屬於任何一個進程,是內核創建的一塊共享的內存 ,生命周期和進程相同, 共享內存是內核的資源會以映射的方式讓進程訪問, 不會使用到進程的資源(要做好互斥), 訪問操作的內存是在內核里,而非進程的
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 , ...) <sys/syscall.h> 或者 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<semaphore.h> :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: 為信號量的初始值
對信號量進行+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
