麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 系統 > Unix > 正文

UNIX環境高級編程筆記

2024-06-28 13:27:56
字體:
來源:轉載
供稿:網友
UNIX環境高級編程筆記

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

以捆綁到另一個套接口上。一般來說本特性僅支持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;}

盡管套接口只有一個,其接收緩沖區和發送緩沖區也分別只有一個,然而這個套接口卻有兩個描述字在引用它:一個在父進程中,

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

close,那么這只是把該引用計數由2減為1,而且既然它仍然大于0,FIN就不發送。這就是使用shutdown函數的另一個理由:即使描述字

的引用計數仍然大于0,FIN也被強迫發送出去。

3.我們啟動客戶/服務器對,然后殺死服務器子進程。這時子進程中所有打開著的描述字都被關閉。這就導致向客戶發送一個FIN(客戶

接收到FIN只是表示服務器進程已經關閉了連接的服務器端,從而不再往其中發送任何數據),而客 戶TCP則相應以一個ACK,1.這時

客戶調用read()讀套接口將返回0。2.若繼續向套接口寫數據,則第一次寫操作將引發RST響應,若第 二次再寫,則內核向該進程發

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

4.如果某個套接口的接收緩沖區中沒有數據可讀,該進程將進入睡眠,直到到達一個以上的字節。如果想要等到某個固定數目的數據可

讀為止,那么可以調用readn(),或者指定MSG_WAITALL標志: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 內核將從應用進程的緩沖區到該套接口的發送緩沖區拷貝數據。對于阻塞的套接口,如果其發送緩沖區中沒有空間,進程將被投入睡

眠,直到有空間為止。對于一個非阻塞TCP套接口,如果其發送緩沖區中根本沒有空間,輸出函數調用將立即返回一個EWOULDBLOCK錯

誤。 UDP套接口不存在真正的發送緩沖區。內核只是拷貝應用進程數據并把它沿協議棧向下傳送,漸次冠以UDP頭部和TCP頭部。因此對于一

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

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

7.非阻塞connect 當在一個非阻塞的TCP套接口上調用connect時,connect將立即返回一個EINPROGROESS錯誤,不過已發起的TCP三路握手繼續進行。 完成一個connect要花一個RTT時間,這段時間也許有我們想要執行的其他處理工作可執行。

8.注意: 盡管套接口是非阻塞的,如果連接到的服務器在同一個主機上,那么當我們調用connect時,連接通常立即建立。我們必須處理這種形 源自Berkeley的實現有關于select和非阻塞connect的兩個規則(1)當連接成功建立時,描述字變為可讀(2)當連接建立遇到錯誤時

,描述字變為可讀又可寫。

程序(直到連接已經建立才返回)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表示連接已經啟動但尚未完成*/ if (errno != EINPROGRESS) return(-1);

/* Do whatever we want while the connect is taking place. */ /*在這里我們可以做想要做的任何事情,可以繼續創建連接*/ 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); /*調用select之前有可能連接已經建立并有來自對端的數據到達。 這種情況下即使套接口上不發生錯誤,套接口也是即可讀又可寫的, 這和連接建立失敗情況下的套接口讀寫條件一樣。 我們通過調用getsockopt并檢查套接口上是否存在待處理錯誤來處理這種情況*/ 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); 設置SO_LINGER套接口選項,把l_onoff標志設置為1,把l_linger時間設置為0.那么當close某個連接時TCP將丟棄保留在套接口發送緩

沖區中的任何數據并發送一個RST給對端,而沒有通常的四分組連接終止序列。

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

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

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

信號的處理方法(1)忽略此信號。但SIGKILL和SIGSTOP不能被忽略,因為它們向超級用戶提供了使進程終止或停止的可靠方法 (2)捕捉信號。不能捕捉SIGKILL和SIGSTOP (3)執行系統默認的動作。大多數信號的系統默認動作是終止進程

當進程掉調用fork時,其子進程繼承父進程的信號處理方式。

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

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

14.可重入函數 不可重入的原因是因為:(1)已知它們使用靜態數據結構(如進程正在執行getpwnam這種將其結果存放在靜態存儲單元中的函數,

期間插入執行信號處理程序,這時會發生不可預見的錯誤)(2)它們調用malloc或free(3)它們是標準I/O函數。標準I/O庫的很多

實現都以不可重入方式使用全局數據結構。

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

些信號進行了排隊。但是除非支持POSIX.1實時擴展,否則大多數UNIX并不對信號排隊。

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

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

進程的信號屏蔽字中。僅當從信號捕捉函數返回時再將進程的信號屏蔽字復位為原先值。這樣,在調用信號處理程序時就能阻塞某些

信號。在信號處理函數中,如果來了同樣的信號,那么新信號會被阻塞,解除后,只調用一次。

18.#include <setjmp.h> sigsetjmp和siglongjmp int sigsetjmp(sigjmp_buf env, int savemask);返回值:若直接調用返回0, 若從siglongjmp調用返回則返回非0值 void siglongjmp(sigjmp_buf env, int val); 如果savemask非0, 則sigsetjmp在env(因為在兩個不同函數中引用,env應為全局變量)中保存了進程的當前信號屏蔽字。調用

siglongjmp時,如果帶非0 savemask的sigsetjmp調用 已經保存了env,則siglongjmp從其中恢復保存的信號屏蔽字。 參數val,將成為從sigsetjmp處返回的值。這樣對一個sigsetjmp可以有多個siglongjmp.

當使用siglongjmp跳回到main函數時,大多數實現并不回滾自動變量和寄存器變量的值。 在信號處理程序中經常調用siglongjmp函數以返回到程序的主循環中,而不是從該處程序返回。

19.volatile sig_atomic_t canjump; 數據類型sig_atomic_t是由ISO C標準定義的變量類型,在寫這種變量類型時不會被中斷。數據類型sig_atomic_t總是包括ISO類型修

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

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

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

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

捕捉到一個信號而且從該信號處理程序返回,則sigsuspend返回,并且將該進程的信號屏蔽字設置為調用sigsuspend之前的值。

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

。 實例

#include <stdio.h>

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

這個程序是錯誤的。原因:buf緩沖區最后一次被清空是在main函數結束之后。但是在此之前buf字符數組已經被釋放。 解決方法:將buf聲明為靜態:static char buf[BUFSIZ]; 或在main函數之外聲明,或使用動態分配堆空間。

22.SIGCLD語義:子進程狀態改變后產生此信號,父進程需要調用一個wait函數以確定發生了什么。 SIGCHLD信號:當一個進程正?;虍惓=K止時,內核就向其父進程發送SIGCHLD信號。

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

R(TASK_RUNNING),可執行狀態: 只有在該狀態的進程才可能在Cpu上運行。而同一時刻可能有多個進程處于可執信狀態,這些進程的task_struct結構被放入Cpu的可

執行隊列中。

S(TASK_INTERRUPTIBLE),可中斷的睡眠狀態: 處于這個狀態的進程因為等待某事件的發生(如等待socket連接,等待信號量),而被掛起。

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

T(TASK_STOPPED or TASK_TRACED),暫停狀態或跟蹤狀態 向進程發送一個SIGSTOP信號,它就會因為響應該信號而進入TASK_STOPPED。當進程正在被跟蹤時,它處于TASK_TRACED。“正在被跟

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

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

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

尸”過程中處于TASK_RUNNING狀態。

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

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

24.pid = 0:是調度進程(swapper) pid = 1:init進程,在自舉過程結束時由內核調用 pid = 2:是頁守護進程。

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

strlen需要一次函數調用(strlen計算長度,碰到null,'/0'為止),而對于sizeof而言,因為緩沖區已用已知字符竄進行了初始化

,其長度是固定的,所以sizeof在編譯時計算緩沖區長度。

26.fork函數

#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);}

執行此程序# ./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函數是不帶緩沖的。因為fork之前調用write,所以其數據寫到標準輸出一次。但是,標準I/O庫是帶緩沖的。如果標準輸出連

到終端設備,則它是行緩沖的,否則它是全緩沖的。當以交互方式運行該程序時,只得到該printf輸出的一次,其原因是標準輸出緩沖

區由換行符沖洗。但是當將標準輸出重定向到一個文件時,卻得到printf輸出兩次。其原因是,在fork之前調用了printf一次,但當調

用fork時,該數據仍在緩沖區中(這時是全緩沖),然后在將附近程數據空間復制到子進程中時,該緩沖區也被復制到子進程中。于是

那時父、子進程各自有了該行內容的標準I/O緩沖區。在exit之前的第二個printf將其數據添加到現有的緩沖區中。當每個進程終止時,

最終會沖洗其緩沖區中的副本。

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

28.vfork函數 它與fork不同,并不將父進程的地址空間完全復制到子進程中,vfork保證子進程先運行,在它調用exec或exit之后父進程才可能被

調度。

29.三種異常終止方式(1)調用abort。它產生SIGABRT信號。(2)當進程接收到某些信號時(3)最后一個線程對“取消”請求做出相應不管進程如何終止,最后都會執行內核中的同一段代碼。這段代碼為相應進程關閉所有打開描述符,釋放他所使用的存儲器等。

30.檢查wait和waitpid所返回的終止狀態的宏 WIFEXITED(status) 若為正常終止子進程返回的狀態,則為真。對此可執行WEXITSTATUS(status),取子進程傳送給exit,

_exit, _Exit參數的低8位 WIFSIGNALED(status) 若為異常終止子進程返回的狀態,則為真(接收到一個不捕捉的信號)。對此,可執行WTERMSIG(status), 取使子進程終止的信號編號。 WIFSTOPPED(status) 若為當前暫停子進程的返回的狀態,則為真。對此,可執行WSTOPSIG(status), 取使子進程終止的信號編號

。 WIFCONTINUED(status) 若在作業控制暫停后已經繼續的子進程返回了狀態,則為真。

31.waitpid的options常量 WCONTINUED 若實現支持作業控制,那么由pid指定的任一子進程在暫停后已經繼續,但其狀態尚未報告,則返回其狀態 WNOHANG 若由pid指定的子進程不是立即可用的,則waitpid不阻塞,此時其返回值為0 WUNTRACED 若某實現支持作業控制,而由pid指定的任一子進程已處于暫停狀態,并且其狀態子暫停以來從未報告過,則返

回其狀態。WIFSTOPPED宏確定返回值是否對應于一個暫停子進程。

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

33.創建了一個進程,使其父進程為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多一個,這與參數rusage有關。該參數要求內核返回由終止進程及其所有子進程使用的資

源匯總:Cpu時間總量、頁面出錯次數、接收到信號的次數等。

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

這些函數返回的新文件描述符與參數filedes共享同一個文件表項。

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

cmdstring的標準輸出。 父進程 cmdstring(子進程) fp <----------------------stdout 執行fp = popen(cmdstring, "r")函數的結果

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

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

如果線程試圖對同一個互斥量加鎖兩次,那么它自身就會陷入死鎖狀態。程序中使用多個互斥量時,也可能出現死鎖。

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

多個線程可以同時占有讀模式的讀寫鎖。 當讀寫鎖在讀加鎖狀態時,所有試圖以讀模式對它進行加鎖的線程都可以得到訪問權,但是如果線程希望以寫模式對此鎖進行加鎖

,它必須阻塞到所有的線程釋放讀鎖,雖然讀寫鎖的實現各不相同,但當讀寫鎖處于讀模式鎖住狀態時,如果有另外的線程試圖以寫模

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

合于對數據結構讀的次數大于寫的情況。

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

程在獲得互斥量之前不會察覺到這種改變,因為必須鎖定互斥量以后才能計算條件。 int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); 傳遞給pthread_cond_wait的互斥量對條件進行保護,調用者把鎖住的互斥量傳遞給函數。函數把調用線程放到等待條件的線程列表

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

樣線程不會錯過條件的任何變化。pthread_cond_wait返回時,互斥量再次被鎖住。

38.restrict關鍵字

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

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

而par并不是訪問數組ar的唯一方式,因此并不能進行下面的優化: par[n]+=8;因為在par[n]+=3前,ar[n]*=2進行了改變。使用了關鍵字restric,編譯器就可以放心地進行優化了。這個關鍵字據說來源于古老的

FORTRAN。

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

errno設置為EPIPE。

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

當打開一個FIFO時,非阻塞標志(O_NONBLOCK)產生下列影響: (1)沒有指定O_NONBLOCK,只讀open要阻塞到其他某個進程為寫而打開此FIFO。類似的,只寫open要阻塞到某個其他進程為讀而打

開它。

一個給定的FIFO有多個寫進程是常見的。常量PIPE_BUF說明了可被原子地寫到FIFO的最大數據量。

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

打開的描述符,但是使該描述符打開就可在客戶數從1變為0時,阻止產生文件終止。打開FIFO兩次需要注意下列操作方式:第一次以非

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

。

#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.標識符和鍵 每個內核中的IPC結構(消息隊列、信號量或共享存儲段)都用一個非負整數的標識符加以引用。IPC不是小的整數。當一個IPC結構

被創建,以后又被刪除時,與這種結構相關的標識符符連續加1,直到達到一個整型數的最大值,然后又會轉到0。

使客戶進程和服務器進程在同一個IPC結構上會和: (1)服務器進程可以指定鍵IPC_PRIVATE創建一個新的IPC結構,將返回的標識符存放在某處(例如一個文件)以便客戶進程取用。 (2)在一個公用頭文件中定義一個客戶進程和服務器進程都認可的鍵。然后服務器進程指定此鍵創建一個IPC結構。 (3)客戶進程和服務器進程認同一個路徑名和項目ID(項目ID是0~255之間的字符值),接著調用函數ftok將這兩個值變換為一個鍵

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

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

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

新的IPC結構: (1)key是IPC_PRIVATE; (2)key當前與特定類型的IPC結構相結合,并且flag中指定了IPC_CREAT位。 為訪問現存的隊列,key必須等于創建該隊列時所指定的鍵,并且不應指定IPC_CREAT。

42.消息隊列 每一個隊列都有一個msqid_ds結構與其相關聯:

打開一個現存隊列或創建一個新隊列。

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

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

44.共享存儲段 它是最快的一種IPC。要注意多個進程之間對一給定存儲區的同步訪問。 創建共享存儲段: int shmget(key_t key, size_t size, int flag);

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

45.unlink()函數 功能描述: 從文件系統中刪除一個名稱,如果名稱是文件的最后一個連接并且沒有其它進程將文件打開,名稱對應的文件會實際被刪除。

用法: #include <unistd.h>

int unlink(const char *pathname);


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 免费毛片儿 | 91网站永久免费看 | 精品一区二区亚洲 | 羞羞草视频 | 中文字幕一区二区三区久久 | 国产91精品欧美 | 亚洲91精品 | 香蕉黄色网 | 男人午夜小视频 | 成人午夜久久 | 7777在线观看 | 国产青草视频在线观看视频 | 亚洲一区二区免费视频 | 澳门一级淫片免费视频 | 看一级大毛片 | 91羞羞| 久久久久免费精品国产小说色大师 | 国产精品久久久久久久久久久久久久久久 | 国产精品一区二区三区在线播放 | av电影网站在线 | 一级尻逼视频 | 精品国产一区二 | 欧美乱码精品一区 | 国产精品.com | 97风流梦电影 | 摸逼逼视频 | 国产一区二区精品91 | 国产精品福利一区 | 激情小说另类 | 午夜伦情电午夜伦情电影 | 欧美成人一二三区 | 91精品久久久久久久久 | 在线观看va | 久久久久免费精品国产小说色大师 | 成人爱爱电影 | 欧美城网站地址 | 激情小说色 | 色呦呦一区二区三区 | 美女福利视频国产 | 羞羞视频免费网站男男 | 久久久久久久高清 |