a亚洲精品_精品国产91乱码一区二区三区_亚洲精品在线免费观看视频_欧美日韩亚洲国产综合_久久久久久久久久久成人_在线区

首頁 > 系統(tǒng) > Unix > 正文

UNIX環(huán)境高級編程筆記

2024-06-28 13:27:56
字體:
供稿:網(wǎng)友
UNIX環(huán)境高級編程筆記

1.setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, len); SO_REUSEADDR套接口選項(xiàng)允許為以下四個(gè)不同的目的提供服務(wù): 一.SO_REUSEADDR允許啟動(dòng)一個(gè)監(jiān)聽服務(wù)器并捆綁其眾所周知的端口,即使以前建立的將該端口用作它們的本地端口的連接仍存在。 二.SO_REUSEADDR允許在同一端口上啟動(dòng)同一服務(wù)器的多個(gè)實(shí)例,只要每個(gè)實(shí)例捆綁一個(gè)不同的本地ip地址即可。 三.SO_REUSEADDR允許單個(gè)進(jìn)程捆綁同一端口到多個(gè)套接口上,只要每次捆綁指定不同的IP地址即可。 四.SO_REUSEADDR允許完全重復(fù)的捆綁:當(dāng)一個(gè)IP地址和端口已捆綁到某個(gè)套接口上時(shí),如果傳輸協(xié)議支持,同樣的IP地址和端口還可

以捆綁到另一個(gè)套接口上。一般來說本特性僅支持UDP接口。

2.程序#include "unp.h"

voidstr_cli(FILE *fp, int sockfd){ pid_t pid; char sendline[MAXLINE], recvline[MAXLINE];

if ( (pid = Fork()) == 0) { /* child: server -> stdout */ while (Readline(sockfd, recvline, MAXLINE) > 0) Fputs(recvline, stdout);

kill(getppid(), SIGTERM); /* in case parent still running */ exit(0); }

/* parent: stdin -> server */ while (Fgets(sendline, MAXLINE, fp) != NULL) Writen(sockfd, sendline, strlen(sendline));

Shutdown(sockfd, SHUT_WR); /* EOF on stdin, send FIN */ pause(); return;}

盡管套接口只有一個(gè),其接收緩沖區(qū)和發(fā)送緩沖區(qū)也分別只有一個(gè),然而這個(gè)套接口卻有兩個(gè)描述字在引用它:一個(gè)在父進(jìn)程中,

另一個(gè)在子進(jìn)程中。 在這里調(diào)用shutdown()而不用close()的原因:套接口描述字是在父子進(jìn)程之間共享的,因此它的引用計(jì)數(shù)為2.要是父進(jìn)程調(diào)用

close,那么這只是把該引用計(jì)數(shù)由2減為1,而且既然它仍然大于0,F(xiàn)IN就不發(fā)送。這就是使用shutdown函數(shù)的另一個(gè)理由:即使描述字

的引用計(jì)數(shù)仍然大于0,FIN也被強(qiáng)迫發(fā)送出去。

3.我們啟動(dòng)客戶/服務(wù)器對,然后殺死服務(wù)器子進(jìn)程。這時(shí)子進(jìn)程中所有打開著的描述字都被關(guān)閉。這就導(dǎo)致向客戶發(fā)送一個(gè)FIN(客戶

接收到FIN只是表示服務(wù)器進(jìn)程已經(jīng)關(guān)閉了連接的服務(wù)器端,從而不再往其中發(fā)送任何數(shù)據(jù)),而客 戶TCP則相應(yīng)以一個(gè)ACK,1.這時(shí)

客戶調(diào)用read()讀套接口將返回0。2.若繼續(xù)向套接口寫數(shù)據(jù),則第一次寫操作將引發(fā)RST響應(yīng),若第 二次再寫,則內(nèi)核向該進(jìn)程發(fā)

送一個(gè)SIGPIPE信號。該信號的缺省行為是終止進(jìn)程,因此進(jìn)程必須捕獲它以免不情愿的被終止。

4.如果某個(gè)套接口的接收緩沖區(qū)中沒有數(shù)據(jù)可讀,該進(jìn)程將進(jìn)入睡眠,直到到達(dá)一個(gè)以上的字節(jié)。如果想要等到某個(gè)固定數(shù)目的數(shù)據(jù)可

讀為止,那么可以調(diào)用readn(),或者指定MSG_WAITALL標(biāo)志:recv(fd, ptr, n, MSG_WAITALL)

#include "unp.h"

ssize_t /* Read "n" bytes from a descriptor. */readn(int fd, void *vptr, size_t n){ size_t nleft; ssize_t nread; char *ptr;

ptr = vptr; nleft = n; while (nleft > 0) { if ( (nread = read(fd, ptr, nleft)) < 0) { if (errno == EINTR) nread = 0; /* and call read() again */ else return(-1); } else if (nread == 0) break; /* EOF */

nleft -= nread; ptr += nread; } return(n - nleft); /* return >= 0 */}

5.非阻塞I/O 輸出操作write, writev, send, sendto, sendmsg 內(nèi)核將從應(yīng)用進(jìn)程的緩沖區(qū)到該套接口的發(fā)送緩沖區(qū)拷貝數(shù)據(jù)。對于阻塞的套接口,如果其發(fā)送緩沖區(qū)中沒有空間,進(jìn)程將被投入睡

眠,直到有空間為止。對于一個(gè)非阻塞TCP套接口,如果其發(fā)送緩沖區(qū)中根本沒有空間,輸出函數(shù)調(diào)用將立即返回一個(gè)EWOULDBLOCK錯(cuò)

誤。 UDP套接口不存在真正的發(fā)送緩沖區(qū)。內(nèi)核只是拷貝應(yīng)用進(jìn)程數(shù)據(jù)并把它沿協(xié)議棧向下傳送,漸次冠以UDP頭部和TCP頭部。因此對于一

個(gè)阻塞的UDP套接口(缺省設(shè)置),輸出函數(shù)調(diào)用將不會(huì)因與TCP套接口一樣的原因而阻塞,不過有可能會(huì)因其他的原因而阻塞。

6.設(shè)置套接口非阻塞: int val = fcntl(sockfd, F_GETFL, 0); fcntl(sockfd, F_SETFL, val | O_NONBLOCK);

7.非阻塞connect 當(dāng)在一個(gè)非阻塞的TCP套接口上調(diào)用connect時(shí),connect將立即返回一個(gè)EINPROGROESS錯(cuò)誤,不過已發(fā)起的TCP三路握手繼續(xù)進(jìn)行。 完成一個(gè)connect要花一個(gè)RTT時(shí)間,這段時(shí)間也許有我們想要執(zhí)行的其他處理工作可執(zhí)行。

8.注意: 盡管套接口是非阻塞的,如果連接到的服務(wù)器在同一個(gè)主機(jī)上,那么當(dāng)我們調(diào)用connect時(shí),連接通常立即建立。我們必須處理這種形 源自Berkeley的實(shí)現(xiàn)有關(guān)于select和非阻塞connect的兩個(gè)規(guī)則(1)當(dāng)連接成功建立時(shí),描述字變?yōu)榭勺x(2)當(dāng)連接建立遇到錯(cuò)誤時(shí)

,描述字變?yōu)榭勺x又可寫。

程序(直到連接已經(jīng)建立才返回)intconnect_nonb(int sockfd, const SA *saptr, socklen_t salen, int nsec){ int flags, n, error; socklen_t len; fd_set rset, wset; struct timeval tval;

flags = Fcntl(sockfd, F_GETFL, 0); Fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);

error = 0; if ( (n = connect(sockfd, saptr, salen)) < 0) /*如果返回EINPROGRESS表示連接已經(jīng)啟動(dòng)但尚未完成*/ if (errno != EINPROGRESS) return(-1);

/* Do whatever we want while the connect is taking place. */ /*在這里我們可以做想要做的任何事情,可以繼續(xù)創(chuàng)建連接*/ if (n == 0) goto done; /* connect completed immediately */

FD_ZERO(&rset); FD_SET(sockfd, &rset); wset = rset; tval.tv_sec = nsec; tval.tv_usec = 0;

if ( (n = Select(sockfd+1, &rset, &wset, NULL, nsec ? &tval : NULL)) == 0) { close(sockfd); /* timeout */ errno = ETIMEDOUT; return(-1); }

if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) { len = sizeof(error); /*調(diào)用select之前有可能連接已經(jīng)建立并有來自對端的數(shù)據(jù)到達(dá)。 這種情況下即使套接口上不發(fā)生錯(cuò)誤,套接口也是即可讀又可寫的, 這和連接建立失敗情況下的套接口讀寫條件一樣。 我們通過調(diào)用getsockopt并檢查套接口上是否存在待處理錯(cuò)誤來處理這種情況*/ if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)/*如果連接成功,該值為0*/ return(-1); /* Solaris pending error */ } else err_quit("select error: sockfd not set");

done: Fcntl(sockfd, F_SETFL, flags); /* restore file status flags */

if (error) { close(sockfd); /* just in case */ errno = error; return(-1); } return(0);}

9.程序 struct linger ling; ling.l_onoff = 1; /* cause RST to be sent on close() */ ling.l_linger = 0; setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling)); close(sockfd); 設(shè)置SO_LINGER套接口選項(xiàng),把l_onoff標(biāo)志設(shè)置為1,把l_linger時(shí)間設(shè)置為0.那么當(dāng)close某個(gè)連接時(shí)TCP將丟棄保留在套接口發(fā)送緩

沖區(qū)中的任何數(shù)據(jù)并發(fā)送一個(gè)RST給對端,而沒有通常的四分組連接終止序列。

10.函數(shù)指針和指針函數(shù)(1)函數(shù)指針是指向函數(shù)的指針變量,int (*f)(int x); /*f指向一個(gè)函數(shù)*/(2)指針函數(shù):函數(shù)返回類型是某一類型的指針:類型標(biāo)識符 *函數(shù)名(函數(shù)表);11.信號 (1)信號是軟件中斷(2)信號提供了一種處理異步事件的方法(3)信號可能會(huì)丟失 linux2.4.22支持31種不同的信號。

不存在編號為0的信號,POSIX.1將此種信號編號值稱為空信號

Ctrl+C鍵通常產(chǎn)生中斷信號(SIGINT)。這是停止一個(gè)已失去控制的程序的方法。

信號的處理方法(1)忽略此信號。但SIGKILL和SIGSTOP不能被忽略,因?yàn)樗鼈兿虺売脩籼峁┝耸惯M(jìn)程終止或停止的可靠方法 (2)捕捉信號。不能捕捉SIGKILL和SIGSTOP (3)執(zhí)行系統(tǒng)默認(rèn)的動(dòng)作。大多數(shù)信號的系統(tǒng)默認(rèn)動(dòng)作是終止進(jìn)程

當(dāng)進(jìn)程掉調(diào)用fork時(shí),其子進(jìn)程繼承父進(jìn)程的信號處理方式。

12.signal函數(shù) #include <signal.h> void (*signal(int signo, void (*func)(int)))(int); 第二個(gè)參數(shù)是函數(shù)指針,它指向的函數(shù)需要一個(gè)整形參數(shù),無返回值。它可以是SIG_IGN, SIG_DEF或自定義函數(shù)

13.不可靠信號 信號阻塞:不要忽略該信號,在其發(fā)生時(shí)記住他。

14.可重入函數(shù) 不可重入的原因是因?yàn)椋海?)已知它們使用靜態(tài)數(shù)據(jù)結(jié)構(gòu)(如進(jìn)程正在執(zhí)行g(shù)etpwnam這種將其結(jié)果存放在靜態(tài)存儲(chǔ)單元中的函數(shù),

期間插入執(zhí)行信號處理程序,這時(shí)會(huì)發(fā)生不可預(yù)見的錯(cuò)誤)(2)它們調(diào)用malloc或free(3)它們是標(biāo)準(zhǔn)I/O函數(shù)。標(biāo)準(zhǔn)I/O庫的很多

實(shí)現(xiàn)都以不可重入方式使用全局?jǐn)?shù)據(jù)結(jié)構(gòu)。

15.如果在解除對某個(gè)信號的阻塞之前,這種信號發(fā)生了多次,POSIX允許系統(tǒng)遞送該信號一次或多次。如果遞送該信號多次,則稱對這

些信號進(jìn)行了排隊(duì)。但是除非支持POSIX.1實(shí)時(shí)擴(kuò)展,否則大多數(shù)UNIX并不對信號排隊(duì)。

16.sigset_t pendmask; if(sigpending(&pendmask) < 0) sigpending()返回信號集 printf("sigpending error/n"); if(sigismember(&pendmask, SIGQUIT)) printf("SIGQUIT pending/n"); 檢查信號SIGQUIT是否未決。

17.sigaction函數(shù) int sigaction(int signo, const struct sigacion *restrict act, struct sigacion *restrict oact); 如果sa_handler包含一個(gè)信號捕捉函數(shù)的地址,則sa_mask字段說明了一個(gè)信號集,在調(diào)用該信號捕捉函數(shù)之前,這一信號集要加到

進(jìn)程的信號屏蔽字中。僅當(dāng)從信號捕捉函數(shù)返回時(shí)再將進(jìn)程的信號屏蔽字復(fù)位為原先值。這樣,在調(diào)用信號處理程序時(shí)就能阻塞某些

信號。在信號處理函數(shù)中,如果來了同樣的信號,那么新信號會(huì)被阻塞,解除后,只調(diào)用一次。

18.#include <setjmp.h> sigsetjmp和siglongjmp int sigsetjmp(sigjmp_buf env, int savemask);返回值:若直接調(diào)用返回0, 若從siglongjmp調(diào)用返回則返回非0值 void siglongjmp(sigjmp_buf env, int val); 如果savemask非0, 則sigsetjmp在env(因?yàn)樵趦蓚€(gè)不同函數(shù)中引用,env應(yīng)為全局變量)中保存了進(jìn)程的當(dāng)前信號屏蔽字。調(diào)用

siglongjmp時(shí),如果帶非0 savemask的sigsetjmp調(diào)用 已經(jīng)保存了env,則siglongjmp從其中恢復(fù)保存的信號屏蔽字。 參數(shù)val,將成為從sigsetjmp處返回的值。這樣對一個(gè)sigsetjmp可以有多個(gè)siglongjmp.

當(dāng)使用siglongjmp跳回到main函數(shù)時(shí),大多數(shù)實(shí)現(xiàn)并不回滾自動(dòng)變量和寄存器變量的值。 在信號處理程序中經(jīng)常調(diào)用siglongjmp函數(shù)以返回到程序的主循環(huán)中,而不是從該處程序返回。

19.volatile sig_atomic_t canjump; 數(shù)據(jù)類型sig_atomic_t是由ISO C標(biāo)準(zhǔn)定義的變量類型,在寫這種變量類型時(shí)不會(huì)被中斷。數(shù)據(jù)類型sig_atomic_t總是包括ISO類型修

飾符volatile,其原因(在信號處理中):該變量將由兩個(gè)不同的線程控制——main函數(shù)和異步執(zhí)行的信號處理程序訪問。

20.sigsuspend函數(shù) 如果在等待信號時(shí)希望去休眠,不正確的方法: if(sigpromask(SIG_SETMASK, &oldmask, NULL) < 0) printf("SIG_SETMASK error/n");

pause(); 如果在解除信號時(shí)刻和pause之間發(fā)生了信號(假設(shè)該信號只有一次),那么在此時(shí)間窗口中發(fā)生的信號就丟失了,使得pause永遠(yuǎn)阻

塞。 如果調(diào)用sigsuspend函數(shù)就非常合適。int sigsuspend(const sigset_t *sigmask); 該函數(shù)先恢復(fù)信號屏蔽字,然后將其設(shè)置為sigmask指向的值,掛起進(jìn)程,此時(shí),除了sigmask指向的值,其他信號都允許通過。如果

捕捉到一個(gè)信號而且從該信號處理程序返回,則sigsuspend返回,并且將該進(jìn)程的信號屏蔽字設(shè)置為調(diào)用sigsuspend之前的值。

21.setbuf(stdout, buf); 該語句將通知輸入輸出庫,所有寫入到stdout的輸出都應(yīng)該使用buf作為輸出緩沖區(qū),直到buf緩沖區(qū)被填滿或程序員直接調(diào)用fflush

。 實(shí)例

#include <stdio.h>

int main(){ int c; char buf[BUFSIZ]; //BUFSIZ由stdio.h頭定義 setbuf(stdout, buf); while((c = getchar()) != EOF) putchar(c);}

這個(gè)程序是錯(cuò)誤的。原因:buf緩沖區(qū)最后一次被清空是在main函數(shù)結(jié)束之后。但是在此之前buf字符數(shù)組已經(jīng)被釋放。 解決方法:將buf聲明為靜態(tài):static char buf[BUFSIZ]; 或在main函數(shù)之外聲明,或使用動(dòng)態(tài)分配堆空間。

22.SIGCLD語義:子進(jìn)程狀態(tài)改變后產(chǎn)生此信號,父進(jìn)程需要調(diào)用一個(gè)wait函數(shù)以確定發(fā)生了什么。 SIGCHLD信號:當(dāng)一個(gè)進(jìn)程正常或異常終止時(shí),內(nèi)核就向其父進(jìn)程發(fā)送SIGCHLD信號。

23.進(jìn)程狀態(tài) R(TASK_RUNNING),可執(zhí)行狀態(tài) S(TASK_INTERRUPTIBLE),可中斷的睡眠狀態(tài) D(TASK_UNINTERRUPTIBLE),不可中斷的睡眠狀態(tài) T(TASK_STOPPED or TASK_TRACED),暫停狀態(tài)或跟蹤狀態(tài) Z(TASK_DEAD-EXIT_ZOMBIE),退出狀態(tài),進(jìn)程成為僵尸進(jìn)程 X(TASK_DEAD-EXIT_DEAD),退出狀態(tài),進(jìn)程即將被銷毀

R(TASK_RUNNING),可執(zhí)行狀態(tài): 只有在該狀態(tài)的進(jìn)程才可能在Cpu上運(yùn)行。而同一時(shí)刻可能有多個(gè)進(jìn)程處于可執(zhí)信狀態(tài),這些進(jìn)程的task_struct結(jié)構(gòu)被放入Cpu的可

執(zhí)行隊(duì)列中。

S(TASK_INTERRUPTIBLE),可中斷的睡眠狀態(tài): 處于這個(gè)狀態(tài)的進(jìn)程因?yàn)榈却呈录陌l(fā)生(如等待socket連接,等待信號量),而被掛起。

D(TASK_UNINTERRUPTIBLE),不可中斷的睡眠狀態(tài) 不可中斷,指的并不是Cpu不響應(yīng)外部硬件中斷,而是指進(jìn)程不響應(yīng)異步信號。 通過代碼 void main() { if(!vfork()) sleep(100); }能夠得到處于TASK_UNINTERRUPTIBLE的進(jìn)程 使用ps -aux | grep a.out查看

T(TASK_STOPPED or TASK_TRACED),暫停狀態(tài)或跟蹤狀態(tài) 向進(jìn)程發(fā)送一個(gè)SIGSTOP信號,它就會(huì)因?yàn)轫憫?yīng)該信號而進(jìn)入TASK_STOPPED。當(dāng)進(jìn)程正在被跟蹤時(shí),它處于TASK_TRACED。“正在被跟

蹤”指進(jìn)程暫停下來,等待跟蹤它的進(jìn)程對它進(jìn)行操作。 Z(TASK_DEAD-EXIT_ZOMBIE),退出狀態(tài),進(jìn)程成為僵尸進(jìn)程 進(jìn)程在退出過程中,處于TASK_DEAD狀態(tài)。在這個(gè)退出過程中,進(jìn)程占有的所有資源將被收回,除了task_struct結(jié)構(gòu)以外。于是進(jìn)程

只剩下task_struct這個(gè)空殼,故稱為僵尸。task_struct保存了進(jìn)程的退出碼,以及一些統(tǒng)計(jì)信息。 父進(jìn)程可通過wait系統(tǒng)調(diào)用(如wait4, waitid)來等待某個(gè)或某些子進(jìn)程的退出,并獲取退出信息。然后把子進(jìn)程的尸體

(task_struct)釋放掉 創(chuàng)造一個(gè)EXIT_ZOMBIE的進(jìn)程: void main() { if(fork()) while(1) sleep(100); } 如果父進(jìn)程先退出,那么就由init進(jìn)程來收尸,init進(jìn)程pid為1,它在等待子進(jìn)程退出的過程中處于TASK_INTERRUPTIBLE狀態(tài),“收

尸”過程中處于TASK_RUNNING狀態(tài)。

X(TASK_DEAD-EXIT_DEAD),退出狀態(tài),進(jìn)程即將被銷毀 進(jìn)程在退出過程中也可能不保留它的task_struct。如這個(gè)進(jìn)程是多線程程序中被detach過的線程。或者父進(jìn)程通過設(shè)置SIGCHLD信號

的handler為SIG_IGN,顯式的忽略了SIGCHLD信號。

24.pid = 0:是調(diào)度進(jìn)程(swapper) pid = 1:init進(jìn)程,在自舉過程結(jié)束時(shí)由內(nèi)核調(diào)用 pid = 2:是頁守護(hù)進(jìn)程。

25.strlen計(jì)算不包含終止null字節(jié)的字符串長度,而sizeof則計(jì)算包括終止null字節(jié)的緩沖區(qū)長度。兩者之間的唯一差別是,使用

strlen需要一次函數(shù)調(diào)用(strlen計(jì)算長度,碰到null,'/0'為止),而對于sizeof而言,因?yàn)榫彌_區(qū)已用已知字符竄進(jìn)行了初始化

,其長度是固定的,所以sizeof在編譯時(shí)計(jì)算緩沖區(qū)長度。

26.fork函數(shù)

#include <stdio.h>#include <unistd.h>

int glob = 6;char buf[] = "a write to stdout/n";

int main(){ int var; pid_t pid;

var = 88; if(write(STDOUT_FILENO, buf, sizeof(buf) - 1) != sizeof(buf) - 1) printf("write error/n"); printf("before fork./n");

if((pid = fork()) < 0){ printf("fork error/n"); }else if(pid == 0){ glob++; var++; }else{ sleep(2); }

printf("pid = %d, glob = %d, var = %d/n", getpid(), glob, var); exit(0);}

執(zhí)行此程序# ./a.outa write to stdoutbefore fork.pid = 13753, glob = 7, var = 89pid = 13752, glob = 6, var = 88

#./a.out > file#cat filea write to stdoutbefore fork.pid = 13756, glob = 7, var = 89before fork.pid = 13755, glob = 6, var = 88

程序解析 write函數(shù)是不帶緩沖的。因?yàn)閒ork之前調(diào)用write,所以其數(shù)據(jù)寫到標(biāo)準(zhǔn)輸出一次。但是,標(biāo)準(zhǔn)I/O庫是帶緩沖的。如果標(biāo)準(zhǔn)輸出連

到終端設(shè)備,則它是行緩沖的,否則它是全緩沖的。當(dāng)以交互方式運(yùn)行該程序時(shí),只得到該printf輸出的一次,其原因是標(biāo)準(zhǔn)輸出緩沖

區(qū)由換行符沖洗。但是當(dāng)將標(biāo)準(zhǔn)輸出重定向到一個(gè)文件時(shí),卻得到printf輸出兩次。其原因是,在fork之前調(diào)用了printf一次,但當(dāng)調(diào)

用fork時(shí),該數(shù)據(jù)仍在緩沖區(qū)中(這時(shí)是全緩沖),然后在將附近程數(shù)據(jù)空間復(fù)制到子進(jìn)程中時(shí),該緩沖區(qū)也被復(fù)制到子進(jìn)程中。于是

那時(shí)父、子進(jìn)程各自有了該行內(nèi)容的標(biāo)準(zhǔn)I/O緩沖區(qū)。在exit之前的第二個(gè)printf將其數(shù)據(jù)添加到現(xiàn)有的緩沖區(qū)中。當(dāng)每個(gè)進(jìn)程終止時(shí),

最終會(huì)沖洗其緩沖區(qū)中的副本。

27.父、子進(jìn)程的關(guān)系 子進(jìn)程繼承父進(jìn)程的文件描述符, (1)父進(jìn)程設(shè)置的文件鎖不會(huì)被子進(jìn)程繼承。(2)子進(jìn)程的未處理鬧鐘(alarm)被清除。(3)子進(jìn)程的未處理信號集設(shè)置為空集

28.vfork函數(shù) 它與fork不同,并不將父進(jìn)程的地址空間完全復(fù)制到子進(jìn)程中,vfork保證子進(jìn)程先運(yùn)行,在它調(diào)用exec或exit之后父進(jìn)程才可能被

調(diào)度。

29.三種異常終止方式(1)調(diào)用abort。它產(chǎn)生SIGABRT信號。(2)當(dāng)進(jìn)程接收到某些信號時(shí)(3)最后一個(gè)線程對“取消”請求做出相應(yīng)不管進(jìn)程如何終止,最后都會(huì)執(zhí)行內(nèi)核中的同一段代碼。這段代碼為相應(yīng)進(jìn)程關(guān)閉所有打開描述符,釋放他所使用的存儲(chǔ)器等。

30.檢查wait和waitpid所返回的終止?fàn)顟B(tài)的宏 WIFEXITED(status) 若為正常終止子進(jìn)程返回的狀態(tài),則為真。對此可執(zhí)行WEXITSTATUS(status),取子進(jìn)程傳送給exit,

_exit, _Exit參數(shù)的低8位 WIFSIGNALED(status) 若為異常終止子進(jìn)程返回的狀態(tài),則為真(接收到一個(gè)不捕捉的信號)。對此,可執(zhí)行WTERMSIG(status), 取使子進(jìn)程終止的信號編號。 WIFSTOPPED(status) 若為當(dāng)前暫停子進(jìn)程的返回的狀態(tài),則為真。對此,可執(zhí)行WSTOPSIG(status), 取使子進(jìn)程終止的信號編號

。 WIFCONTINUED(status) 若在作業(yè)控制暫停后已經(jīng)繼續(xù)的子進(jìn)程返回了狀態(tài),則為真。

31.waitpid的options常量 WCONTINUED 若實(shí)現(xiàn)支持作業(yè)控制,那么由pid指定的任一子進(jìn)程在暫停后已經(jīng)繼續(xù),但其狀態(tài)尚未報(bào)告,則返回其狀態(tài) WNOHANG 若由pid指定的子進(jìn)程不是立即可用的,則waitpid不阻塞,此時(shí)其返回值為0 WUNTRACED 若某實(shí)現(xiàn)支持作業(yè)控制,而由pid指定的任一子進(jìn)程已處于暫停狀態(tài),并且其狀態(tài)子暫停以來從未報(bào)告過,則返

回其狀態(tài)。WIFSTOPPED宏確定返回值是否對應(yīng)于一個(gè)暫停子進(jìn)程。

32.waitpid和wait的區(qū)別 (1)waitpid可等待一個(gè)特定的進(jìn)程 (2)提供了一個(gè)wait的非阻塞版本 (3)支持作業(yè)控制

33.創(chuàng)建了一個(gè)進(jìn)程,使其父進(jìn)程為init

#include <stdio.h>#include <unistd.h>

int main(){ pid_t pid;

if((pid =fork()) < 0){ printf("fork error./n"); } else if(pid == 0){ if((pid = fork()) < 0) printf("fork error./n"); else if(pid > 0) exit(0);

sleep(2); printf("second child, parent pid = %d/n", getppid()); exit(0); }

if(waitpid(pid, NULL, 0) != pid) printf("waitpid error./n");

exit(0);}

34.wait3和wait4 pid_t wait4(pid_t pid, int *statloc, int options, struct rusage *rusage); 它們提供的功能比wait, waitpid, waitid多一個(gè),這與參數(shù)rusage有關(guān)。該參數(shù)要求內(nèi)核返回由終止進(jìn)程及其所有子進(jìn)程使用的資

源匯總:Cpu時(shí)間總量、頁面出錯(cuò)次數(shù)、接收到信號的次數(shù)等。

35.dup2函數(shù) int dup2(int filedes, int filedes2); 若成功返回新的描述符,若出錯(cuò)則返回-1. 如果filedes2已經(jīng)打開,則先將其關(guān)閉。如若filefdes等于filedes2,則dup2返回filefdes2,而不關(guān)閉它。

這些函數(shù)返回的新文件描述符與參數(shù)filedes共享同一個(gè)文件表項(xiàng)。

36.fopen和fclose函數(shù) 常見的操作是創(chuàng)建一個(gè)管道連接到另一個(gè)進(jìn)程,然后讀其輸出或者向其輸入端發(fā)送數(shù)據(jù)。 原型:FILE *popen(const char *cmdstring, const char *type); 函數(shù)popen先執(zhí)行fork,然后調(diào)用exec以執(zhí)行cmdstring,并且返回一個(gè)標(biāo)準(zhǔn)I/O文件指針。如果type是"r",則文件指針連接到

cmdstring的標(biāo)準(zhǔn)輸出。 父進(jìn)程 cmdstring(子進(jìn)程) fp <----------------------stdout 執(zhí)行fp = popen(cmdstring, "r")函數(shù)的結(jié)果

37.線程同步(1)互斥量。 互斥量用pthread_mutex_t數(shù)據(jù)類型來表示,在使用互斥變量以前,必須首先對它進(jìn)行初始化,可以把他置為常量

PTHREA_MUTEX_INITIALIZER(只對靜態(tài)分配的互斥量)。 #include <pthread.h> int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); 要用默認(rèn)的屬性初始化互斥量,只需把a(bǔ)ttr設(shè)置為Null。

如果線程試圖對同一個(gè)互斥量加鎖兩次,那么它自身就會(huì)陷入死鎖狀態(tài)。程序中使用多個(gè)互斥量時(shí),也可能出現(xiàn)死鎖。

(2)讀寫鎖。 讀寫鎖可以有三種狀態(tài):讀模式下加鎖狀態(tài),寫模式下加鎖狀態(tài),不加鎖狀態(tài)。一次只有一個(gè)線程可以占有寫模式的讀寫鎖,但是

多個(gè)線程可以同時(shí)占有讀模式的讀寫鎖。 當(dāng)讀寫鎖在讀加鎖狀態(tài)時(shí),所有試圖以讀模式對它進(jìn)行加鎖的線程都可以得到訪問權(quán),但是如果線程希望以寫模式對此鎖進(jìn)行加鎖

,它必須阻塞到所有的線程釋放讀鎖,雖然讀寫鎖的實(shí)現(xiàn)各不相同,但當(dāng)讀寫鎖處于讀模式鎖住狀態(tài)時(shí),如果有另外的線程試圖以寫模

式加鎖,讀寫鎖通常會(huì)阻塞隨后的讀模式鎖請求。這樣可以避免讀模式鎖長期占用,而等待的寫模式鎖一直得不到滿足。讀寫鎖非常適

合于對數(shù)據(jù)結(jié)構(gòu)讀的次數(shù)大于寫的情況。

(3)條件變量 允許線程以無競爭的方式等待特定的條件發(fā)生。條件本身是由互斥量保護(hù)的。線程在改變條件狀態(tài)前必須首先鎖住互斥量,其他線

程在獲得互斥量之前不會(huì)察覺到這種改變,因?yàn)楸仨氭i定互斥量以后才能計(jì)算條件。 int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); 傳遞給pthread_cond_wait的互斥量對條件進(jìn)行保護(hù),調(diào)用者把鎖住的互斥量傳遞給函數(shù)。函數(shù)把調(diào)用線程放到等待條件的線程列表

上,然后對互斥量解鎖,這兩個(gè)操作是原子操作。這樣就關(guān)閉了條件檢查和線程進(jìn)入睡眠等待條件改變這兩個(gè)操作之間的時(shí)間通道,這

樣線程不會(huì)錯(cuò)過條件的任何變化。pthread_cond_wait返回時(shí),互斥量再次被鎖住。

38.restrict關(guān)鍵字

restrict是c99引入的,它只可以用于限定指針,并表明指針是訪問一個(gè)數(shù)據(jù)對象的唯一且初始的方式,考慮下面的例子:int ar[10];int * restrict restar=(int *)malloc(10*sizeof(int));int *par=ar;

這里說明restar是訪問由malloc()分配的內(nèi)存的唯一且初始的方式。par就不是了。那么:for(n=0;n<10;n++){ par[n]+=5; restar[n]+=5; ar[n]*=2; par[n]+=3; restar[n]+=3;}因?yàn)閞estar是訪問分配的內(nèi)存的唯一且初始的方式,那么編譯器可以將上述對restar的操作進(jìn)行優(yōu)化: restar[n]+=8;

而par并不是訪問數(shù)組ar的唯一方式,因此并不能進(jìn)行下面的優(yōu)化: par[n]+=8;因?yàn)樵趐ar[n]+=3前,ar[n]*=2進(jìn)行了改變。使用了關(guān)鍵字restric,編譯器就可以放心地進(jìn)行優(yōu)化了。這個(gè)關(guān)鍵字據(jù)說來源于古老的

FORTRAN。

39.管道(1)當(dāng)讀一個(gè)寫端已被關(guān)閉的管道時(shí),在所有數(shù)據(jù)都被讀取后,read返回0,以指示達(dá)到了文件結(jié)束處。(2)如果寫一個(gè)讀端已被關(guān)閉的管道,則產(chǎn)生信號SIGPIPE。如果忽略該信號或者捕捉該信號并從其處理程序返回,則write返回-1,

errno設(shè)置為EPIPE。

40.FIFO #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); 創(chuàng)建FIFO類似于創(chuàng)建文件。創(chuàng)建完成后可用open打開它。

當(dāng)打開一個(gè)FIFO時(shí),非阻塞標(biāo)志(O_NONBLOCK)產(chǎn)生下列影響: (1)沒有指定O_NONBLOCK,只讀open要阻塞到其他某個(gè)進(jìn)程為寫而打開此FIFO。類似的,只寫open要阻塞到某個(gè)其他進(jìn)程為讀而打

開它。

一個(gè)給定的FIFO有多個(gè)寫進(jìn)程是常見的。常量PIPE_BUF說明了可被原子地寫到FIFO的最大數(shù)據(jù)量。

UNIX環(huán)境高級編程 第二版P415。 為讀寫方式打開FIFO,POSIX.1特別聲明沒有為讀寫而打開FIFO。解決方法就是打開FIFO兩次,一次讀一次寫。我們不會(huì)使用為寫而

打開的描述符,但是使該描述符打開就可在客戶數(shù)從1變?yōu)?時(shí),阻止產(chǎn)生文件終止。打開FIFO兩次需要注意下列操作方式:第一次以非

阻塞只讀方式open,第二次以阻塞、只寫方式open。(如果先用非阻塞、只寫方式open將返回錯(cuò)誤。)然后關(guān)閉讀描述符的非阻塞屬性

#include "apue.h"#include <fcntl.h>

#define FIFO "temp.fifo"

int main(){ int fdread, fdwrite; unlink(FIFO);

if(mkfifo(FIFO, FILE_MODE) < 0) printf("error/n"); if((fdread = open(FIFO, O_RDONLY | O_NONBLOCK)) < 0) printf("error/n"); if((fdwrite = open(FIFO, O_WRONLY)) < 0) printf("error/n");

clr_fl(fdread, O_NONBLOCK); exit(0);}

41.標(biāo)識符和鍵 每個(gè)內(nèi)核中的IPC結(jié)構(gòu)(消息隊(duì)列、信號量或共享存儲(chǔ)段)都用一個(gè)非負(fù)整數(shù)的標(biāo)識符加以引用。IPC不是小的整數(shù)。當(dāng)一個(gè)IPC結(jié)構(gòu)

被創(chuàng)建,以后又被刪除時(shí),與這種結(jié)構(gòu)相關(guān)的標(biāo)識符符連續(xù)加1,直到達(dá)到一個(gè)整型數(shù)的最大值,然后又會(huì)轉(zhuǎn)到0。

使客戶進(jìn)程和服務(wù)器進(jìn)程在同一個(gè)IPC結(jié)構(gòu)上會(huì)和: (1)服務(wù)器進(jìn)程可以指定鍵IPC_PRIVATE創(chuàng)建一個(gè)新的IPC結(jié)構(gòu),將返回的標(biāo)識符存放在某處(例如一個(gè)文件)以便客戶進(jìn)程取用。 (2)在一個(gè)公用頭文件中定義一個(gè)客戶進(jìn)程和服務(wù)器進(jìn)程都認(rèn)可的鍵。然后服務(wù)器進(jìn)程指定此鍵創(chuàng)建一個(gè)IPC結(jié)構(gòu)。 (3)客戶進(jìn)程和服務(wù)器進(jìn)程認(rèn)同一個(gè)路徑名和項(xiàng)目ID(項(xiàng)目ID是0~255之間的字符值),接著調(diào)用函數(shù)ftok將這兩個(gè)值變換為一個(gè)鍵

。然后在方法2中使用這些鍵。

#include<sys/ipc.h> key_t ftok(const char *path, int id);

三個(gè)get函數(shù)(msgget, semget, shmget)都有兩個(gè)類似的參數(shù):一個(gè)key和一個(gè)整型flag。如若滿足下列兩個(gè)條件之一,則創(chuàng)建一個(gè)

新的IPC結(jié)構(gòu): (1)key是IPC_PRIVATE; (2)key當(dāng)前與特定類型的IPC結(jié)構(gòu)相結(jié)合,并且flag中指定了IPC_CREAT位。 為訪問現(xiàn)存的隊(duì)列,key必須等于創(chuàng)建該隊(duì)列時(shí)所指定的鍵,并且不應(yīng)指定IPC_CREAT。

42.消息隊(duì)列 每一個(gè)隊(duì)列都有一個(gè)msqid_ds結(jié)構(gòu)與其相關(guān)聯(lián):

打開一個(gè)現(xiàn)存隊(duì)列或創(chuàng)建一個(gè)新隊(duì)列。

#include <sys/msg.h> int msgget(key_t key, int flag);

43.信號量 為了獲得共享資源,進(jìn)程需要執(zhí)行下列操作:(1)測試控制該資源的信號量。(2)若此信號量的值為正,則進(jìn)程可以使用該資源。進(jìn)程將信號量值減1,表示它使用了一個(gè)資源單位。(3)若此信號量的值為0,則進(jìn)程進(jìn)入休眠狀態(tài),直至信號量值大于0.

44.共享存儲(chǔ)段 它是最快的一種IPC。要注意多個(gè)進(jìn)程之間對一給定存儲(chǔ)區(qū)的同步訪問。 創(chuàng)建共享存儲(chǔ)段: int shmget(key_t key, size_t size, int flag);

一旦創(chuàng)建了一個(gè)共享存儲(chǔ)段,進(jìn)程就可調(diào)用shmat將其連接到它的地址空間中: void *shmat(int shmid, const void *addr, int flag); 若成功則返回指向共享存儲(chǔ)的指針,出錯(cuò)-1. 共享存儲(chǔ)段僅緊靠在棧之下。

45.unlink()函數(shù) 功能描述: 從文件系統(tǒng)中刪除一個(gè)名稱,如果名稱是文件的最后一個(gè)連接并且沒有其它進(jìn)程將文件打開,名稱對應(yīng)的文件會(huì)實(shí)際被刪除。

用法: #include <unistd.h>

int unlink(const char *pathname);


發(fā)表評論 共有條評論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 在线看一区二区 | 日韩在线观看中文字幕 | 日本久草 | 国产欧美精品 | 中文字幕成人 | 99re6在线视频精品免费 | 免费av片 | 国产综合99 | 日日骚| 亚洲欧美另类久久久精品2019 | 日本一区二区久久 | 久久精品视频免费观看 | 国产精品久久久久久久久久 | 色婷婷综合久久久久中文一区二 | 在线看av的网址 | 国产精品1区2区 | 中文字幕在线免费 | 亚洲高清免费 | 日本久久精品 | 国产精品免费av | 久久久久久网站 | 色橹橹欧美在线观看视频高清 | 欧美日韩国产在线 | 中文字幕一区二区三区不卡 | 在线免费毛片 | 成人精品一区二区三区电影黑人 | 日本黄色短片 | 亚洲一区中文字幕在线观看 | 欧美精品在线一区 | 国产一区二区三区精品在线 | 四虎av在线| 国产高清视频在线 | 国产不卡福利片 | 91偷拍精品一区二区三区 | 国产一区视频在线 | 在线视频这里只有精品 | 国产高清av在线一区二区三区 | 91亚洲视频在线观看 | 成人免费一区二区三区视频网站 | 久久久久久久一区二区 | 在线成人一区 |